[rkward-cvs] SF.net SVN: rkward:[3156] branches/2010_10_18_backend_restructuring_branch/ rkward
tfry at users.sourceforge.net
tfry at users.sourceforge.net
Wed Oct 27 13:04:15 UTC 2010
Revision: 3156
http://rkward.svn.sourceforge.net/rkward/?rev=3156&view=rev
Author: tfry
Date: 2010-10-27 13:04:14 +0000 (Wed, 27 Oct 2010)
Log Message:
-----------
Copy (simplified) commands to the backend, instead of sharing them between threads. Bye bye mutex hell.
Modified Paths:
--------------
branches/2010_10_18_backend_restructuring_branch/rkward/agents/showedittextfileagent.cpp
branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommand.cpp
branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommand.h
branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommandstack.cpp
branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommandstack.h
branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.cpp
branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.h
branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rinterface.cpp
branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rinterface.h
branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rpackages/rkward/R/internal.R
branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rthread.cpp
Modified: branches/2010_10_18_backend_restructuring_branch/rkward/agents/showedittextfileagent.cpp
===================================================================
--- branches/2010_10_18_backend_restructuring_branch/rkward/agents/showedittextfileagent.cpp 2010-10-26 19:02:49 UTC (rev 3155)
+++ branches/2010_10_18_backend_restructuring_branch/rkward/agents/showedittextfileagent.cpp 2010-10-27 13:04:14 UTC (rev 3156)
@@ -134,10 +134,8 @@
}
}
- MUTEX_LOCK;
// this line is what causes the backend-thread to resume processing:
args->done = true;
- MUTEX_UNLOCK;
deleteLater ();
}
Modified: branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommand.cpp
===================================================================
--- branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommand.cpp 2010-10-26 19:02:49 UTC (rev 3155)
+++ branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommand.cpp 2010-10-27 13:04:14 UTC (rev 3156)
@@ -166,3 +166,44 @@
}
return ret;
}
+
+
+
+RCommandProxy::RCommandProxy (RCommand *from) : RData () {
+ RK_TRACE (RBACKEND);
+ RK_ASSERT (from);
+
+ command = from->_command;
+ type = from->_type;
+ id = from->_id;
+ status = from->status;
+ RK_ASSERT (status == 0); // Initialization from an already touched command is not a real problem, but certainly no expected usage
+ RK_ASSERT (from->datatype == RData::NoData);
+}
+
+RCommandProxy::RCommandProxy (const QString &command, int type) {
+ RK_TRACE (RBACKEND);
+
+ RCommandProxy::command = command;
+ RCommandProxy::type = type;
+ RK_ASSERT (type & RCommand::Internal);
+ id = -1;
+ status = 0;
+}
+
+RCommandProxy::~RCommandProxy () {
+ RK_TRACE (RBACKEND);
+
+ RK_ASSERT ((type & RCommand::Internal) || (datatype == RData::NoData));
+}
+
+void RCommandProxy::mergeAndDelete (RCommand *to) {
+ RK_TRACE (RBACKEND);
+ RK_ASSERT (to);
+ RK_ASSERT (to->_id == id);
+ RK_ASSERT (to->_type == type);
+
+ to->status = status;
+ to->setData (*this);
+ delete this;
+}
Modified: branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommand.h
===================================================================
--- branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommand.h 2010-10-26 19:02:49 UTC (rev 3155)
+++ branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommand.h 2010-10-27 13:04:14 UTC (rev 3156)
@@ -29,6 +29,7 @@
class RCommandReceiver;
class RCommand;
+class RCommandProxy;
class RCommandChain;
/** Base class for RCommand and RCommandChain, to make it possible to store both in the same list */
@@ -104,7 +105,7 @@
@param receiver The RCommandReceiver this command should be passed on to, when finished.
@param flags A freely assignable integer, that you can use to identify what the command was all about. Only the RCommandReceiver handling the results will have to know what exactly the flags mean.
*/
- explicit RCommand (const QString &command, int type = 0, const QString &rk_equiv = QString::null, RCommandReceiver *receiver=0, int flags=0);
+ explicit RCommand (const QString &command, int type, const QString &rk_equiv = QString::null, RCommandReceiver *receiver=0, int flags=0);
/** destructor. Note: you should not delete RCommands manually. This is done in RInterface. TODO: make protected */
~RCommand();
/** @returns the type as specified in RCommand::RCommand */
@@ -186,11 +187,8 @@
ROutputList &getOutput () { return output_list; };
/** modify the command string. DO NOT CALL THIS after the command has been submitted! */
void setCommand (const QString &command) { _command = command; };
-
-/** public for internal reasons, only. Don't modify outside the rbackend classes. */
- int status;
private:
-friend class RThread;
+friend class RCommandProxy;
friend class RInterface;
friend class RCommandStack;
friend class RCommandStackModel;
@@ -202,10 +200,29 @@
QString _command;
int _type;
int _flags;
+ int status;
QString _rk_equiv;
int _id;
static int next_id;
RCommandReceiver *receivers[MAX_RECEIVERS_PER_RCOMMAND];
};
+/** This is a reduced version of an RCommand, intended for use in the R backend. */
+class RCommandProxy : public RData {
+public:
+/** creates a proxy for the given RCommand */
+ RCommandProxy (RCommand *from);
+ ~RCommandProxy ();
+/** update the given RCommand with the status / data of the proxy command. */
+ void mergeAndDelete (RCommand *to);
+protected:
+friend class RThread;
+ RCommandProxy (const QString &command, int type);
+public: // all these are public for technical reasons, only.
+ QString command;
+ int type;
+ int id;
+ int status;
+};
+
#endif
Modified: branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommandstack.cpp
===================================================================
--- branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommandstack.cpp 2010-10-26 19:02:49 UTC (rev 3155)
+++ branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommandstack.cpp 2010-10-27 13:04:14 UTC (rev 3156)
@@ -95,11 +95,9 @@
RK_TRACE (RBACKEND);
if (RK_Debug_CommandStep) {
- MUTEX_UNLOCK;
QTime t;
t.start ();
while (t.elapsed () < RK_Debug_CommandStep) {}
- MUTEX_LOCK;
}
clearFinishedChains ();
@@ -199,13 +197,6 @@
static_model = this;
listeners = 0;
- have_mutex_lock = false;
-
- connect (this, SIGNAL (itemAboutToBeAdded(RCommandBase*)), this, SLOT (relayItemAboutToBeAdded(RCommandBase*)), Qt::BlockingQueuedConnection);
- connect (this, SIGNAL (itemAdded()), this, SLOT (relayItemAdded()), Qt::BlockingQueuedConnection);
- connect (this, SIGNAL (itemAboutToBeRemoved(RCommandBase*)), this, SLOT (relayItemAboutToBeRemoved(RCommandBase*)), Qt::BlockingQueuedConnection);
- connect (this, SIGNAL (itemRemoved()), this, SLOT (relayItemRemoved()), Qt::BlockingQueuedConnection);
- connect (this, SIGNAL (itemChanged(RCommandBase*)), this, SLOT (relayItemChanged(RCommandBase*)), Qt::BlockingQueuedConnection);
}
RCommandStackModel::~RCommandStackModel () {
@@ -217,7 +208,6 @@
void RCommandStackModel::addListener () {
RK_TRACE (RBACKEND);
- lockMutex (); // this should make sure, a listener is not added in the middle of a beginRowsXXX and endRowsXXX signal pair
++listeners;
}
@@ -226,13 +216,12 @@
RK_TRACE (RBACKEND);
--listeners;
- if (!listeners) unlockMutex ();
+ RK_ASSERT (listeners >= 0);
}
QModelIndex RCommandStackModel::index (int row, int column, const QModelIndex& parent) const {
RK_ASSERT (listeners > 0);
RK_TRACE (RBACKEND);
- lockMutex ();
RCommandBase* index_data = 0;
@@ -264,7 +253,6 @@
QModelIndex RCommandStackModel::parent (const QModelIndex& child) const {
RK_ASSERT (listeners);
RK_TRACE (RBACKEND);
- lockMutex ();
RCommandBase* index_data;
if (!child.isValid()) {
@@ -289,7 +277,6 @@
int RCommandStackModel::rowCount (const QModelIndex& parent) const {
RK_ASSERT (listeners);
RK_TRACE (RBACKEND);
- lockMutex ();
if (!parent.isValid ()) return 1;
@@ -315,7 +302,6 @@
int RCommandStackModel::columnCount (const QModelIndex&) const {
RK_ASSERT (listeners);
RK_TRACE (RBACKEND);
- lockMutex ();
return NUM_COLS;
}
@@ -323,7 +309,6 @@
QVariant RCommandStackModel::data (const QModelIndex& index, int role) const {
RK_ASSERT (listeners);
RK_TRACE (RBACKEND);
- lockMutex ();
if (!index.isValid ()) return QVariant ();
RK_ASSERT (index.model () == this);
@@ -377,7 +362,6 @@
Qt::ItemFlags RCommandStackModel::flags (const QModelIndex& index) const {
RK_ASSERT (listeners);
RK_TRACE (RBACKEND);
- lockMutex ();
if (!index.isValid ()) return 0;
RK_ASSERT (index.model () == this);
@@ -392,7 +376,6 @@
QVariant RCommandStackModel::headerData (int section, Qt::Orientation orientation, int role) const {
RK_ASSERT (listeners);
RK_TRACE (RBACKEND);
- lockMutex ();
if ((orientation == Qt::Horizontal) && (role == Qt::DisplayRole)) {
if (section == MAIN_COL) return (i18n ("Command"));
@@ -428,97 +411,22 @@
if (!listeners) return;
RK_TRACE (RBACKEND);
- if (RInterface::inRThread ()) {
- MUTEX_UNLOCK; // release the mutex in the R thread, as the main thread will need it.
- emit (itemAboutToBeRemoved (parent));
- MUTEX_LOCK;
- } else {
- relayItemAboutToBeRemoved (parent);
- }
+ QModelIndex parent_index = indexFor (parent);
+ // items are always removed at the front
+ beginRemoveRows (parent_index, 0, 0);
}
void RCommandStackModel::popComplete () {
if (!listeners) return;
RK_TRACE (RBACKEND);
- if (RInterface::inRThread ()) {
- MUTEX_UNLOCK; // release the mutex in the R thread, as the main thread will need it.
- emit (itemRemoved ());
- MUTEX_LOCK;
- } else {
- relayItemRemoved ();
- }
+ endRemoveRows ();
}
void RCommandStackModel::aboutToAdd (RCommandBase* parent) {
if (!listeners) return;
RK_TRACE (RBACKEND);
- if (RInterface::inRThread ()) {
- MUTEX_UNLOCK; // release the mutex in the R thread, as the main thread will need it.
- emit (itemAboutToBeAdded (parent));
- MUTEX_LOCK;
- } else {
- relayItemAboutToBeAdded (parent);
- }
-}
-
-void RCommandStackModel::addComplete () {
- if (!listeners) return;
- RK_TRACE (RBACKEND);
-
- if (RInterface::inRThread ()) {
- MUTEX_UNLOCK; // release the mutex in the R thread, as the main thread will need it.
- emit (itemAdded ());
- MUTEX_LOCK;
- } else {
- relayItemAdded ();
- }
-}
-
-void RCommandStackModel::itemChange (RCommandBase* item) {
- if (!listeners) return;
- RK_TRACE (RBACKEND);
-
- if (RInterface::inRThread ()) {
- MUTEX_UNLOCK; // release the mutex in the R thread, as the main thread will need it.
- emit (itemChanged (item));
- MUTEX_LOCK;
- } else {
- relayItemChanged (item);
- }
-}
-
-void RCommandStackModel::lockMutex () const {
- if (have_mutex_lock) return;
- RK_TRACE (RBACKEND);
-
- RK_ASSERT (!RInterface::inRThread ());
-
- MUTEX_LOCK;
-// We're playing silly const games, here, as the reimplementations from QAbstractItemModel need to be const.
-// Well, we're not really changing anything, though, just keeping track of the mutex lock.
- bool *cheat = const_cast<bool*> (&have_mutex_lock);
- *cheat = true;
-
- QTimer::singleShot (0, const_cast<RCommandStackModel*> (this), SLOT (unlockMutex()));
-}
-
-void RCommandStackModel::unlockMutex () {
- if (!have_mutex_lock) return;
- RK_TRACE (RBACKEND);
-
- RK_ASSERT (!RInterface::inRThread ());
-
- MUTEX_UNLOCK;
- have_mutex_lock = false;
-}
-
-void RCommandStackModel::relayItemAboutToBeAdded (RCommandBase* parent) {
- RK_TRACE (RBACKEND);
-
- RK_ASSERT (!RInterface::inRThread ());
-
QModelIndex parent_index = indexFor (parent);
if ((!parent) || parent->commandPointer ()) {
beginInsertRows (parent_index, 0, 0);
@@ -529,37 +437,17 @@
}
}
-void RCommandStackModel::relayItemAdded () {
+void RCommandStackModel::addComplete () {
+ if (!listeners) return;
RK_TRACE (RBACKEND);
- RK_ASSERT (!RInterface::inRThread ());
-
endInsertRows ();
}
-void RCommandStackModel::relayItemAboutToBeRemoved (RCommandBase* parent) {
+void RCommandStackModel::itemChange (RCommandBase* item) {
+ if (!listeners) return;
RK_TRACE (RBACKEND);
- RK_ASSERT (!RInterface::inRThread ());
-
- QModelIndex parent_index = indexFor (parent);
- // items are always removed at the front
- beginRemoveRows (parent_index, 0, 0);
-}
-
-void RCommandStackModel::relayItemRemoved () {
- RK_TRACE (RBACKEND);
-
- RK_ASSERT (!RInterface::inRThread ());
-
- endRemoveRows ();
-}
-
-void RCommandStackModel::relayItemChanged (RCommandBase* item) {
- RK_TRACE (RBACKEND);
-
- RK_ASSERT (!RInterface::inRThread ());
-
QModelIndex item_index = indexFor (item);
emit (dataChanged (item_index, item_index));
}
Modified: branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommandstack.h
===================================================================
--- branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommandstack.h 2010-10-26 19:02:49 UTC (rev 3155)
+++ branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rcommandstack.h 2010-10-27 13:04:14 UTC (rev 3156)
@@ -2,7 +2,7 @@
rcommandstack - description
-------------------
begin : Mon Sep 6 2004
- copyright : (C) 2004, 2007 by Thomas Friedrichsmeier
+ copyright : (C) 2004, 2007, 2010 by Thomas Friedrichsmeier
email : tfry at users.sourceforge.net
***************************************************************************/
@@ -123,38 +123,10 @@
/** call this, when you have made changes to an item, that should be reflected in RControlWindow
@param item The item that was changed */
void itemChange (RCommandBase* item);
-private slots:
- /** compagnon to lockMutex () */
- void unlockMutex ();
- /** the purpose of these signals and slots is to make sure the aboutToPop(), popComplete(), aboutToAdd(), addComplete(), and itemChange() methods are *always* dealt with in the main (GUI) thread. */
- void relayItemAboutToBeAdded (RCommandBase* parent);
- /** @see relayItemAboutToBeAdded() */
- void relayItemAdded ();
- /** @see relayItemAboutToBeAdded() */
- void relayItemAboutToBeRemoved (RCommandBase* parent);
- /** @see relayItemAboutToBeAdded() */
- void relayItemRemoved ();
- /** @see relayItemAboutToBeAdded() */
- void relayItemChanged (RCommandBase* item);
-signals:
- /** @see relayItemAboutToBeAdded() */
- void itemAboutToBeAdded (RCommandBase* parent);
- /** @see relayItemAboutToBeAdded() */
- void itemAdded ();
- /** @see relayItemAboutToBeAdded() */
- void itemAboutToBeRemoved (RCommandBase* parent);
- /** @see relayItemAboutToBeAdded() */
- void itemRemoved ();
- /** @see relayItemAboutToBeAdded() */
- void itemChanged (RCommandBase* item);
private:
- /** locks the mutex temporarily (until the event loop is next entered again), and only, if not already locked */
- void lockMutex () const;
/** number of listeners. If there are no listeners, the model will do almost nothing at all */
int listeners;
static RCommandStackModel* static_model;
- /** @see lockMutex() */
- bool have_mutex_lock;
/** create a model index for the given item */
QModelIndex indexFor (RCommandBase *item);
Modified: branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.cpp
===================================================================
--- branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.cpp 2010-10-26 19:02:49 UTC (rev 3155)
+++ branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.cpp 2010-10-27 13:04:14 UTC (rev 3156)
@@ -30,10 +30,10 @@
#include "../debug.h"
#include "rkrsupport.h"
+#include "rinterface.h"
#include "rklocalesupport.h"
#include "rkpthreadsupport.h"
#include "rksignalsupport.h"
-#include "rinterface.h" // for access to the mutex
#include "../misc/rkcommonfunctions.h"
#include <stdlib.h>
@@ -189,15 +189,13 @@
if (RThread::this_pointer->killed) return 0;
if (RThread::repl_status.user_command_status == RThread::RKReplStatus::NoUserCommand) {
- RCommand *command = RThread::this_pointer->fetchNextCommand ();
+ RCommandProxy *command = RThread::this_pointer->fetchNextCommand ();
if (!command) {
return 0; // jumps out of the event loop!
}
- MUTEX_LOCK;
- if (!(command->type () & RCommand::User)) {
+ if (!(command->type & RCommand::User)) {
RThread::this_pointer->runCommand (command);
- MUTEX_UNLOCK;
RThread::this_pointer->commandFinished ();
} else {
// so, we are about to transmit a new user command, which is quite a complex endeavour...
@@ -214,8 +212,7 @@
RThread::repl_status.user_command_completely_transmitted = false;
RThread::repl_status.user_command_parsed_up_to = 0;
RThread::repl_status.user_command_successful_up_to = 0;
- RThread::repl_status.user_command_buffer = RThread::this_pointer->current_locale_codec->fromUnicode (command->command ());
- MUTEX_UNLOCK;
+ RThread::repl_status.user_command_buffer = RThread::this_pointer->current_locale_codec->fromUnicode (command->command);
RKTransmitNextUserCommandChunk (buf, buflen);
RThread::repl_status.user_command_status = RThread::RKReplStatus::UserCommandTransmitted;
return 1;
@@ -228,20 +225,17 @@
if (RThread::this_pointer->current_locale_codec->toUnicode (prompt) == RKRSupport::SEXPToString (Rf_GetOption (Rf_install ("continue"), R_BaseEnv))) {
incomplete = true;
}
- MUTEX_LOCK;
if (incomplete) RThread::this_pointer->current_command->status |= RCommand::Failed | RCommand::ErrorIncomplete;
RThread::repl_status.user_command_status = RThread::RKReplStatus::ReplIterationKilled;
- MUTEX_UNLOCK;
+#warning TODO: use Rf_error(""), instead?
RK_doIntr (); // to discard the buffer
} else {
RKTransmitNextUserCommandChunk (buf, buflen);
return 1;
}
} else if (RThread::repl_status.user_command_status == RThread::RKReplStatus::UserCommandSyntaxError) {
- MUTEX_LOCK;
RThread::this_pointer->current_command->status |= RCommand::Failed | RCommand::ErrorSyntax;
RThread::repl_status.user_command_status = RThread::RKReplStatus::NoUserCommand;
- MUTEX_UNLOCK;
RThread::this_pointer->commandFinished ();
} else if (RThread::repl_status.user_command_status == RThread::RKReplStatus::UserCommandRunning) {
// it appears, the user command triggered a call to readline.
@@ -249,20 +243,17 @@
if (n_frames < 1) {
// No active frames? This is either a browser() call at toplevel, or R jumped us back to toplevel, behind our backs.
// For safety, let's reset and start over.
- MUTEX_LOCK;
RThread::this_pointer->current_command->status |= RCommand::Failed | RCommand::ErrorOther;
RThread::repl_status.user_command_status = RThread::RKReplStatus::ReplIterationKilled;
- MUTEX_UNLOCK;
+#warning TODO: use Rf_error(""), instead?
RK_doIntr (); // to discard the buffer
} else {
// Handled below
break;
}
} else if (RThread::repl_status.user_command_status == RThread::RKReplStatus::UserCommandFailed) {
- MUTEX_LOCK;
RThread::this_pointer->current_command->status |= RCommand::Failed | RCommand::ErrorOther;
RThread::repl_status.user_command_status = RThread::RKReplStatus::NoUserCommand;
- MUTEX_UNLOCK;
RThread::this_pointer->commandFinished ();
} else {
RK_ASSERT (RThread::repl_status.user_command_status == RThread::RKReplStatus::ReplIterationKilled);
@@ -830,7 +821,7 @@
RKInsertToplevelStatementFinishedCallback (0);
// get info on R runtime version
- RCommand *dummy = runDirectCommand ("as.numeric (R.version$major) * 1000 + as.numeric (R.version$minor) * 10", RCommand::GetIntVector);
+ RCommandProxy *dummy = runDirectCommand ("as.numeric (R.version$major) * 1000 + as.numeric (R.version$minor) * 10", RCommand::GetIntVector);
if ((dummy->getDataType () == RData::IntVector) && (dummy->getDataLength () == 1)) {
r_version = dummy->getIntVector ()[0];
} else {
@@ -935,42 +926,36 @@
bool RThread::runDirectCommand (const QString &command) {
RK_TRACE (RBACKEND);
- RCommand c (command, RCommand::App | RCommand::Sync | RCommand::Internal);
+ RCommandProxy c (command, RCommand::App | RCommand::Sync | RCommand::Internal);
runCommand (&c);
- return (c.succeeded ());
+ return ((c.status & RCommand::WasTried) && !(c.status & RCommand::Failed));
}
-RCommand *RThread::runDirectCommand (const QString &command, RCommand::CommandTypes datatype) {
+RCommandProxy *RThread::runDirectCommand (const QString &command, RCommand::CommandTypes datatype) {
RK_TRACE (RBACKEND);
RK_ASSERT ((datatype >= RCommand::GetIntVector) && (datatype <= RCommand::GetStructuredData));
- RCommand *c = new RCommand (command, RCommand::App | RCommand::Sync | RCommand::Internal | datatype);
+ RCommandProxy *c = new RCommandProxy (command, RCommand::App | RCommand::Sync | RCommand::Internal | datatype);
runCommand (c);
return c;
}
-void RThread::runCommand (RCommand *command) {
+void RThread::runCommand (RCommandProxy *command) {
RK_TRACE (RBACKEND);
RK_ASSERT (command);
RKWardRError error = NoError;
- // NOTE the command must not be accessed while the mutex is unlocked!
- // Therefore we copy the data we need, and create a container for the returned data
- int ctype = command->type ();
- QString ccommand = command->command ();
+ int ctype = command->type; // easier typing
RData retdata;
// running user commands is quite different from all other commands and should have been handled by RReadConsole
RK_ASSERT (!(ctype & RCommand::User));
- if (!(ctype & RCommand::Internal)) {
- MUTEX_UNLOCK;
- }
if (ctype & RCommand::DirectToOutput) runDirectCommand (".rk.capture.messages()");
repl_status.eval_depth++;
- SEXP parsed = parseCommand (ccommand, &error);
+ SEXP parsed = parseCommand (command->command, &error);
if (error == NoError) {
SEXP exp;
PROTECT (exp = runCommandInternalBase (parsed, &error));
@@ -997,35 +982,19 @@
if (ctype & RCommand::DirectToOutput) runDirectCommand (".rk.print.captured.messages()");
if (!(ctype & RCommand::Internal)) {
if (!RInterface::backendIsLocked () || killed) processX11Events ();
- MUTEX_LOCK;
}
command->setData (retdata);
// common error/status handling
- #ifdef RKWARD_DEBUG
- int dl = DL_WARNING; // failed application commands are an issue worth reporting, failed user commands are not
- if (command->type () & RCommand::User) dl = DL_DEBUG;
- #endif
if (error != NoError) {
command->status |= RCommand::WasTried | RCommand::Failed;
if (error == Incomplete) {
command->status |= RCommand::ErrorIncomplete;
- RK_DO (qDebug ("Command failed (incomplete)"), RBACKEND, dl);
} else if (error == SyntaxError) {
command->status |= RCommand::ErrorSyntax;
- RK_DO (qDebug ("Command failed (syntax)"), RBACKEND, dl);
- } else if (command->status & RCommand::Canceled) {
- RK_DO (qDebug ("Command failed (interrupted)"), RBACKEND, dl);
- } else {
+ } else if (!(command->status & RCommand::Canceled)) {
command->status |= RCommand::ErrorOther;
- #ifdef RKWARD_DEBUG
- dl = DL_WARNING; // always interested in strange errors
- #endif
- RK_DO (qDebug ("Command failed (other)"), RBACKEND, dl);
}
- RK_DO (qDebug ("failed command was: '%s'", command->command ().toLatin1 ().data ()), RBACKEND, dl);
-/* flushOutput ();
- RK_DO (qDebug ("- error message was: '%s'", command->error ().toLatin1 ().data ()), RBACKEND, dl); */
} else {
command->status |= RCommand::WasTried;
}
Modified: branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.h
===================================================================
--- branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.h 2010-10-26 19:02:49 UTC (rev 3155)
+++ branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.h 2010-10-27 13:04:14 UTC (rev 3156)
@@ -77,7 +77,7 @@
friend class RInterface;
friend class RThread;
bool *done;
- RCommand *command;
+ RCommandProxy *command;
};
/** Simple event class to relay information from the RThread to the main thread. This is basically like QCustomEvent in Qt3*/
@@ -176,8 +176,8 @@
/** convenience low-level function for running a command, directly. Use this overload, if you want to handle a return value.
@param command command to be runCommand
@param datatype the data type that should be (attempted to be) returned
- at returns a pointer to the RCommand-instance that was created and used, internally. You can query this pointer for status and data. Be sure to delete it, when done. */
- RCommand *runDirectCommand (const QString &command, RCommand::CommandTypes datatype);
+ at returns a pointer to the RCommandProxy-instance that was created and used, internally. You can query this pointer for status and data. Be sure to delete it, when done. */
+ RCommandProxy *runDirectCommand (const QString &command, RCommand::CommandTypes datatype);
public:
/** call this periodically to make R's x11 windows process their events */
static void processX11Events ();
@@ -199,9 +199,9 @@
void handleStandardCallback (RCallbackArgs *args);
/** The command currently being executed. */
- RCommand *current_command;
+ RCommandProxy *current_command;
- void runCommand (RCommand *command);
+ void runCommand (RCommandProxy *command);
/** only one instance of this class may be around. This pointer keeps the reference to it, for interfacing to from C to C++ */
static RThread *this_pointer;
@@ -238,7 +238,7 @@
static RKReplStatus repl_status;
// fetch next command (and do event processing while waiting)
- RCommand *fetchNextCommand ();
+ RCommandProxy *fetchNextCommand ();
void commandFinished (bool check_object_updates_needed=true);
/** thread is killed. Should exit as soon as possible. @see kill */
bool killed;
@@ -258,10 +258,6 @@
/** the main loop. See \ref RThread for a more detailed description */
void run ();
private:
-/** This is the function in which an RCommand actually gets processed. Basically it passes the command to runCommand () and sends RInterface some events about what is currently happening. */
- void doCommand (RCommand *command);
- void notifyCommandDone (RCommand *command);
-
/** A copy of the names of the toplevel environments (as returned by "search ()"). */
QStringList toplevel_env_names;
/** A copy of the names of the toplevel symbols in the .GlobalEnv. */
@@ -270,7 +266,7 @@
QStringList changed_symbol_names;
/** check wether the object list / global environment / individual symbols have changed, and updates them, if needed */
void checkObjectUpdatesNeeded (bool check_list);
- QList<RCommand*> all_current_commands;
+ QList<RCommandProxy*> all_current_commands;
/** current output */
ROutputList output_buffer;
Modified: branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rinterface.cpp
===================================================================
--- branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rinterface.cpp 2010-10-26 19:02:49 UTC (rev 3155)
+++ branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rinterface.cpp 2010-10-27 13:04:14 UTC (rev 3156)
@@ -60,12 +60,6 @@
// update output (for immediate output commands) at least this often (msecs):
#define FLUSH_INTERVAL 100
-//static
-QMutex RInterface::mutex (QMutex::Recursive);
-#ifdef DEBUG_MUTEX
- int RInterface::mutex_counter;
-#endif // DEBUG_MUTEX
-
RInterface::RInterface () {
RK_TRACE (RBACKEND);
@@ -84,18 +78,17 @@
command_logfile_mode = NotRecordingCommands;
command_request = 0;
previously_idle = false;
- locked = Startup;
+ locked = 0;
- r_thread = new RThread ();
-
// create a fake init command
RCommand *fake = new RCommand (i18n ("R Startup"), RCommand::App | RCommand::Sync | RCommand::ObjectListUpdate, i18n ("R Startup"));
issueCommand (fake);
- all_current_commands.append (fake);
flush_timer = new QTimer (this);
connect (flush_timer, SIGNAL (timeout ()), this, SLOT (flushOutput ()));
flush_timer->start (FLUSH_INTERVAL);
+
+ r_thread = new RThread ();
}
void RInterface::issueCommand (const QString &command, int type, const QString &rk_equiv, RCommandReceiver *receiver, int flags, RCommandChain *chain) {
@@ -132,9 +125,7 @@
RK_TRACE (RBACKEND);
bool idle;
- MUTEX_LOCK;
- idle = (RCommandStack::regular_stack->isEmpty() && (!r_thread->current_command));
- MUTEX_UNLOCK;
+ idle = (RCommandStack::regular_stack->isEmpty() && (!runningCommand()));
return (idle);
}
@@ -162,7 +153,8 @@
}
RCommand *RInterface::runningCommand () {
- return r_thread->current_command;
+ if (all_current_commands.isEmpty ()) return 0;
+ return (all_current_commands.last ());
}
void RInterface::tryNextCommand () {
@@ -188,7 +180,7 @@
}
stack->pop ();
// notify ourselves...
- RKRBackendEvent* event = new RKRBackendEvent (RKRBackendEvent::RCommandOut, command);
+ RKRBackendEvent* event = new RKRBackendEvent (RKRBackendEvent::RCommandOut, new RCommandProxy (command));
qApp->postEvent (this, event);
return;
}
@@ -216,7 +208,10 @@
RK_ASSERT (command_request);
flushOutput (true);
+ RCommandProxy *proxy = 0;
if (command) {
+ proxy = new RCommandProxy (command);
+
RK_DO (qDebug ("running command: %s", command->command ().toLatin1().data ()), RBACKEND, DL_DEBUG);
command->status |= RCommand::Running;
RCommandStackModel::getModel ()->itemChange (command);
@@ -231,7 +226,7 @@
}
}
- command_request->command = command;
+ command_request->command = proxy;
*(command_request->done) = true;
command_request = 0;
QThread::yieldCurrentThread ();
@@ -255,12 +250,31 @@
} else if (ev->etype () == RKRBackendEvent::RCommandOut) {
flushOutput (true);
- RCommand *command = static_cast <RCommand *> (ev->data ());
+ RCommandProxy *cproxy = static_cast <RCommandProxy *> (ev->data ());
RK_ASSERT (!all_current_commands.isEmpty ());
- RK_ASSERT (all_current_commands.last () == command);
- all_current_commands.pop_back ();
+ RCommand *command = all_current_commands.takeLast ();
RCommandStack::currentStack ()->pop ();
+ cproxy->mergeAndDelete (command);
+ #ifdef RKWARD_DEBUG
+ int dl = DL_WARNING; // failed application commands are an issue worth reporting, failed user commands are not
+ if (command->type () & RCommand::User) dl = DL_DEBUG;
+ if (command->failed ()) {
+ command->status |= RCommand::WasTried | RCommand::Failed;
+ if (command->status & RCommand::ErrorIncomplete) {
+ RK_DO (qDebug ("Command failed (incomplete)"), RBACKEND, dl);
+ } else if (command->status & RCommand::ErrorSyntax) {
+ RK_DO (qDebug ("Command failed (syntax)"), RBACKEND, dl);
+ } else if (command->status & RCommand::Canceled) {
+ RK_DO (qDebug ("Command failed (interrupted)"), RBACKEND, dl);
+ } else {
+ RK_DO (qDebug ("Command failed (other)"), RBACKEND, dl);
+ }
+ RK_DO (qDebug ("failed command was: '%s'", qPrintable (command->command ())), RBACKEND, dl);
+ RK_DO (qDebug ("- error message was: '%s'", qPrintable (command->error ())), RBACKEND, dl);
+ }
+ #endif
+
if (command->status & RCommand::Canceled) {
command->status |= RCommand::HasError;
ROutput *out = new ROutput;
@@ -276,13 +290,15 @@
}
}
command->finished ();
+ RCommandStackModel::getModel ()->itemChange (command);
delete command;
} else if ((ev->etype () == RKRBackendEvent::REvalRequest)) {
processREvalRequest (static_cast<REvalRequest *> (ev->data ()));
} else if ((ev->etype () == RKRBackendEvent::RCallbackRequest)) {
processRCallbackRequest (static_cast<RCallbackArgs *> (ev->data ()));
} else if ((ev->etype () == RKRBackendEvent::RStarted)) {
- locked -= locked & Startup;
+ bool* ok_to_proceed = static_cast<bool*> (ev->data ());
+ *ok_to_proceed = true;
RKWardMainWindow::discardStartupOptions ();
} else if ((ev->etype () == RKRBackendEvent::RStartupError)) {
flushOutput (true);
@@ -325,7 +341,7 @@
// RK_TRACE (RBACKEND);
ROutputList list = r_thread->flushOutput (forced);
-qDebug ("fo %d", list.size ());
+
foreach (ROutput *output, list) {
if (all_current_commands.isEmpty ()) {
RK_DO (qDebug ("output without receiver'%s'", qPrintable (output->output)), RBACKEND, DL_WARNING);
@@ -368,34 +384,29 @@
void RInterface::issueCommand (RCommand *command, RCommandChain *chain) {
RK_TRACE (RBACKEND);
- MUTEX_LOCK;
+
if (command->command ().isEmpty ()) command->_type |= RCommand::EmptyCommand;
RCommandStack::issueCommand (command, chain);
tryNextCommand ();
- MUTEX_UNLOCK;
}
RCommandChain *RInterface::startChain (RCommandChain *parent) {
RK_TRACE (RBACKEND);
+
RCommandChain *ret;
- MUTEX_LOCK;
ret = RCommandStack::startChain (parent);
- MUTEX_UNLOCK;
return ret;
};
void RInterface::closeChain (RCommandChain *chain) {
RK_TRACE (RBACKEND);
- MUTEX_LOCK;
RCommandStack::closeChain (chain);
tryNextCommand ();
- MUTEX_UNLOCK;
};
void RInterface::cancelCommand (RCommand *command) {
RK_TRACE (RBACKEND);
- MUTEX_LOCK;
if (!(command->type () & RCommand::Sync)) {
command->status |= RCommand::Canceled;
@@ -412,7 +423,6 @@
}
RCommandStackModel::getModel ()->itemChange (command);
- MUTEX_UNLOCK;
}
void RInterface::pauseProcessing (bool pause) {
@@ -493,16 +503,12 @@
} else if (call == "startOpenX11") {
// TODO: error checking/handling (wrong parameter count/type)
if (request->call.count () >= 2) {
- MUTEX_LOCK;
window_catcher->start (QString (request->call[1]).toInt ());
- MUTEX_UNLOCK;
}
} else if (call == "endOpenX11") {
// TODO: error checking/handling (wrong parameter count/type)
if (request->call.count () >= 2) {
- MUTEX_LOCK;
window_catcher->stop (QString (request->call[1]).toInt ());
- MUTEX_UNLOCK;
}
} else if (call == "updateDeviceHistory") {
if (request->call.count () >= 2) {
@@ -657,7 +663,7 @@
bool dummy_command = false;
RCommand *command = runningCommand ();
if (!command) {
- command = new RCommand ("");
+ command = new RCommand ("", RCommand::EmptyCommand);
dummy_command = true;
}
Modified: branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rinterface.h
===================================================================
--- branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rinterface.h 2010-10-26 19:02:49 UTC (rev 3155)
+++ branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rinterface.h 2010-10-27 13:04:14 UTC (rev 3156)
@@ -24,15 +24,6 @@
#include "rcommand.h"
-//#define DEBUG_MUTEX
-#ifdef DEBUG_MUTEX
-#define MUTEX_LOCK RInterface::mutex.lock (); qDebug ("mutex locks: %d, locked in %s, %s, %d", ++RInterface::mutex_counter, __FILE__, __FUNCTION__, __LINE__);
-#define MUTEX_UNLOCK qDebug ("mutex locks: %d, unlocked in %s, %s, %d", --RInterface::mutex_counter, __FILE__, __FUNCTION__, __LINE__); RInterface::mutex.unlock ();
-#else
-#define MUTEX_LOCK RInterface::mutex.lock ();
-#define MUTEX_UNLOCK RInterface::mutex.unlock ();
-#endif
-
class RCommand;
class RKWardMainWindow;
struct RCallbackArgs;
@@ -80,12 +71,6 @@
/** Pauses process. The current command will continue to run, but no new command will be */
void pauseProcessing (bool pause);
-/** *The* mutex in usein RKWard. This is needed to ensure, the main (GUI) thread, and the backend thread (@see RThread) do not try to access the same data at the same time. Use MUTEX_LOCK and MUTEX_UNLOCK to lock/unlock the mutex. */
- static QMutex mutex;
-#ifdef DEBUG_MUTEX
- static int mutex_counter;
-#endif
-
/** returns the command currently running in the thread. Be careful when using the returned pointer! */
RCommand *runningCommand ();
@@ -132,8 +117,7 @@
/** @see locked */
enum LockType {
User=1, /**< locked on user request */
- Cancel=2, /**< locked to safely cancel a running command */
- Startup=4 /**< locked on startup */
+ Cancel=2 /**< locked to safely cancel a running command */
};
/** Used for locking the backend, meaning not further commands will be given to the backend. This is used, when the currently running command is to be cancelled. It is used to make sure that the backend thread does not proceed with further commands, before the main thread takes notice. Also it is called, if the RThread is paused on User request. Further, the thread is initially locked so the main thread can check for some conditions before the backend thread may produce
Modified: branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rpackages/rkward/R/internal.R
===================================================================
--- branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rpackages/rkward/R/internal.R 2010-10-26 19:02:49 UTC (rev 3155)
+++ branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rpackages/rkward/R/internal.R 2010-10-27 13:04:14 UTC (rev 3156)
@@ -334,7 +334,7 @@
}
# for caputring message output while running a plugin command
-.rk.capture.messages <- function () {
+".rk.capture.messages" <- function () {
if (exists (".rk.capture.messages.sinkfile", envir=as.environment ("package:rkward"), inherits=FALSE)) {
# We don't support nesting, so purge it, first
.rk.print.captured.messages ()
@@ -346,7 +346,7 @@
assign (".rk.capture.messages.sinknumber", sink.number ("message"), envir=as.environment ("package:rkward"))
}
-.rk.print.captured.messages <- function (clear=TRUE) {
+".rk.print.captured.messages" <- function (clear=TRUE) {
if (!exists (".rk.capture.messages.sinkfile", envir=as.environment ("package:rkward"), inherits=FALSE)) return ()
sinkfile <- get (".rk.capture.messages.sinkfile", envir=as.environment ("package:rkward"), inherits=FALSE)
@@ -366,6 +366,8 @@
warning ("Message sink has been removed, already.")
} else {
sink (type="message") # remove it
+ con <- getConnection (sinknumber)
+ con.close ()
}
if (file.exists (sinkfile)) file.remove (sinkfile)
remove (list=".rk.capture.messages.sinkfile", envir=as.environment ("package:rkward"))
Modified: branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rthread.cpp
===================================================================
--- branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rthread.cpp 2010-10-26 19:02:49 UTC (rev 3155)
+++ branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rthread.cpp 2010-10-27 13:04:14 UTC (rev 3156)
@@ -58,22 +58,19 @@
killed = false;
int err;
- // in RInterface::RInterface() we create a fake RCommand to capture all the output/errors during startup
- MUTEX_LOCK;
- current_command = RCommandStack::regular_stack->currentCommand ();
- all_current_commands.append (current_command);
- RK_ASSERT (current_command);
- MUTEX_UNLOCK;
+ // in RInterface::RInterface() we have created a fake RCommand to capture all the output/errors during startup. Fetch it
+ fetchNextCommand ();
if ((err = initialize ())) {
int* err_c = new int;
*err_c = err;
qApp->postEvent (RKGlobals::rInterface (), new RKRBackendEvent (RKRBackendEvent::RStartupError, err_c));
}
- qApp->postEvent (RKGlobals::rInterface (), new RKRBackendEvent (RKRBackendEvent::RStarted));
+ bool startup_was_handled = false;
+ qApp->postEvent (RKGlobals::rInterface (), new RKRBackendEvent (RKRBackendEvent::RStarted, &startup_was_handled));
// wait until RKWard is set to go (esp, it has handled any errors during startup, etc.)
- while (RInterface::backendIsLocked ()) {
+ while (!startup_was_handled) {
msleep (10);
}
@@ -86,22 +83,22 @@
RK_TRACE (RBACKEND);
RK_DO (qDebug ("done running command"), RBACKEND, DL_DEBUG);
- MUTEX_LOCK;
+
current_command->status -= (current_command->status & RCommand::Running);
current_command->status |= RCommand::WasTried;
- RCommandStackModel::getModel ()->itemChange (current_command);
- if (check_object_updates_needed || (current_command->type () & RCommand::ObjectListUpdate)) {
- checkObjectUpdatesNeeded (current_command->type () & (RCommand::User | RCommand::ObjectListUpdate));
+ if (check_object_updates_needed || (current_command->type & RCommand::ObjectListUpdate)) {
+ checkObjectUpdatesNeeded (current_command->type & (RCommand::User | RCommand::ObjectListUpdate));
}
- notifyCommandDone (current_command); // command may be deleted after this
+ RKRBackendEvent* event = new RKRBackendEvent (RKRBackendEvent::RCommandOut, current_command);
+ qApp->postEvent (RKGlobals::rInterface (), event); // command may be deleted after this!
+
all_current_commands.pop_back();
if (!all_current_commands.isEmpty ()) current_command = all_current_commands.last ();
- MUTEX_UNLOCK;
}
-RCommand* RThread::fetchNextCommand () {
+RCommandProxy* RThread::fetchNextCommand () {
RK_TRACE (RBACKEND);
RNextCommandRequest req;
@@ -118,7 +115,7 @@
if (!done) msleep (10);
}
- RCommand *command = req.command;
+ RCommandProxy *command = req.command;
if (command) {
all_current_commands.append (command);
current_command = command;
@@ -127,17 +124,6 @@
return command;
}
-void RThread::notifyCommandDone (RCommand *command) {
- RK_TRACE (RBACKEND);
-
- RK_ASSERT (command == current_command);
- current_command = 0;
-
- // notify GUI-thread that command was finished
- RKRBackendEvent* event = new RKRBackendEvent (RKRBackendEvent::RCommandOut, command);
- qApp->postEvent (RKGlobals::rInterface (), event);
-}
-
void RThread::waitIfOutputBufferExceeded () {
// don't trace
while (out_buf_len > MAX_BUF_LENGTH) {
@@ -149,6 +135,7 @@
RK_TRACE (RBACKEND);
if (!buf_length) return;
+ RK_DO (qDebug ("Output type %d: ", output_type, qPrintable (output)), RBACKEND, DL_DEBUG);
waitIfOutputBufferExceeded ();
output_buffer_mutex.lock ();
@@ -199,7 +186,7 @@
if (call.count () == 2) { // schedule symbol update for later
if (call[0] == "ws") {
// always keep in mind: No current command can happen for tcl/tk events.
- if ((!current_command) || (current_command->type () & RCommand::ObjectListUpdate) || (!(current_command->type () & RCommand::Sync))) { // ignore Sync commands that are not flagged as ObjectListUpdate
+ if ((!current_command) || (current_command->type & RCommand::ObjectListUpdate) || (!(current_command->type & RCommand::Sync))) { // ignore Sync commands that are not flagged as ObjectListUpdate
if (!changed_symbol_names.contains (call[1])) changed_symbol_names.append (call[1]);
}
return;
@@ -211,11 +198,9 @@
RKRBackendEvent* event = new RKRBackendEvent (RKRBackendEvent::REvalRequest, &request);
qApp->postEvent (RKGlobals::rInterface (), event);
- RCommand *c;
+ RCommandProxy *c;
while ((c = fetchNextCommand ())) {
- MUTEX_LOCK;
runCommand (c);
- MUTEX_UNLOCK;
commandFinished (false);
}
}
@@ -257,8 +242,8 @@
if (!runDirectCommand (".rk.fix.assignments ()\n")) status |= LibLoadFail;
// find out about standard library locations
- RCommand *dummy = runDirectCommand (".libPaths ()\n", RCommand::GetStringVector);
- if (dummy->failed ()) status |= OtherFail;
+ RCommandProxy *dummy = runDirectCommand (".libPaths ()\n", RCommand::GetStringVector);
+ if (dummy->status & RCommand::Failed) status |= OtherFail;
for (unsigned int i = 0; i < dummy->getDataLength (); ++i) {
RKSettingsModuleRPackages::defaultliblocs.append (dummy->getStringVector ()[i]);
}
@@ -266,7 +251,7 @@
// start help server / determined help base url
dummy = runDirectCommand (".rk.getHelpBaseUrl ()\n", RCommand::GetStringVector);
- if (dummy->failed ()) status |= OtherFail;
+ if (dummy->status & RCommand::Failed) status |= OtherFail;
else {
RK_ASSERT (dummy->getDataLength () == 1);
RKSettingsModuleR::help_base_url = dummy->getStringVector ()[0];
@@ -296,14 +281,13 @@
/* NOTE: We're keeping separate lists of the items on the search path, and the toplevel symbols in .GlobalEnv here.
This info is also present in RObjectList (and it's children). However: a) in a less convenient form, b) in the other thread. To avoid locking, and other complexity, keeping separate lists seems an ok solution. Keep in mind that only the names of only the toplevel objects are kept, here, so the memory overhead should be minimal */
- MUTEX_UNLOCK;
bool search_update_needed = false;
bool globalenv_update_needed = false;
if (check_list) {
// TODO: avoid parsing this over and over again
RK_DO (qDebug ("checkObjectUpdatesNeeded: getting search list"), RBACKEND, DL_TRACE);
- RCommand *dummy = runDirectCommand ("search ()\n", RCommand::GetStringVector);
+ RCommandProxy *dummy = runDirectCommand ("search ()\n", RCommand::GetStringVector);
if ((int) dummy->getDataLength () != toplevel_env_names.count ()) {
search_update_needed = true;
} else {
@@ -368,5 +352,4 @@
handleSubstackCall (call);
changed_symbol_names.clear ();
}
- MUTEX_LOCK;
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
More information about the rkward-tracker
mailing list