Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Changeset 3307 for code


Ignore:
Timestamp:
Jul 19, 2009, 2:12:50 AM (15 years ago)
Author:
landauf
Message:

Completely rewrote TclThreadManager. This fixes several bugs from the initial implementation. The main features work fine now, but some tasks are still open (for example destroying a thread or implementing a queue size limit).

I hope this doesn't cause any troubles because I used the TclThreadManager-version from netp6 as a draft for the reimplementation. I've also applied the c-style-cast-fixes from core.

I tested this with boost 1.37, I hope it also works with 1.35 (I haven't seen any remarkable changes in the log). However it won't work with boost 1.34.1 and lower, but this change already happened back in netp6, so please update your dependencies if you're still using an old version (the current release is 1.39 btw).

Location:
code/trunk/src/core
Files:
1 added
6 edited

Legend:

Unmodified
Added
Removed
  • code/trunk/src/core/CorePrereqs.h

    r3304 r3307  
    150150    class SubclassIdentifier;
    151151    class TclBind;
     152    struct TclInterpreterBundle;
     153    template <class T>
     154    class TclThreadList;
    152155    class TclThreadManager;
    153156    class Template;
     
    201204// Boost
    202205namespace boost
    203 { 
     206{
    204207    namespace filesystem
    205208    {
  • code/trunk/src/core/Game.cc

    r3304 r3307  
    246246
    247247            // UPDATE, Core first
     248            bool threwException = false;
    248249            try
    249250            {
    250251                this->core_->update(*this->gameClock_);
    251252            }
     253            catch (const std::exception& ex)
     254            {
     255                threwException = true;
     256                COUT(0) << "Exception while ticking the Core: " << ex.what() << std::endl;
     257            }
    252258            catch (...)
     259            {
     260                threwException = true;
     261            }
     262            if (threwException)
    253263            {
    254264                COUT(0) << "An exception occured while ticking the Core. This should really never happen!" << std::endl;
  • code/trunk/src/core/IRC.cc

    r3304 r3307  
    5353    {
    5454        unsigned int threadID = IRC_TCL_THREADID;
    55         TclThreadManager::createID(threadID);
    56         this->interpreter_ = TclThreadManager::getInstance().getTclInterpreter(threadID);
     55        this->interpreter_ = TclThreadManager::createWithId(threadID);
    5756
    5857        try
  • code/trunk/src/core/TclBind.cc

    r3280 r3307  
    7979            this->interpreter_->def("orxonox::crossquery", TclThreadManager::tcl_crossquery, Tcl::variadic());
    8080            this->interpreter_->def("execute", TclBind::tcl_execute, Tcl::variadic());
     81            this->interpreter_->def("orxonox::crossexecute", TclThreadManager::tcl_crossexecute, Tcl::variadic());
    8182
    8283            try
    8384            {
    84                 this->interpreter_->eval("proc query args { orxonox::query $args }");
    85                 this->interpreter_->eval("proc crossquery {id args} { orxonox::crossquery 0 $id $args }");
     85                this->interpreter_->eval("proc query args { orxonox::query [join $args] }");
     86                this->interpreter_->eval("proc crossquery {id args} { orxonox::crossquery 0 $id [join $args] }");
     87                this->interpreter_->eval("proc crossexecute {id args} { orxonox::crossquery 0 $id [join $args] }");
    8688                this->interpreter_->eval("set id 0");
    8789                this->interpreter_->eval("rename exit tcl::exit; proc exit {} { execute exit }");
  • code/trunk/src/core/TclThreadManager.cc

    r3304 r3307  
    3030
    3131#include <boost/bind.hpp>
    32 #include <boost/thread/condition.hpp>
    33 #include <boost/thread/mutex.hpp>
    3432#include <boost/thread/thread.hpp>
     33#include <boost/thread/locks.hpp>
     34#include <boost/thread/shared_mutex.hpp>
    3535#include <OgreTimer.h>
    3636#include <cpptcl/cpptcl.h>
    3737
    3838#include "util/Convert.h"
    39 #include "util/Debug.h"
    4039#include "Clock.h"
    4140#include "CommandExecutor.h"
     
    4342#include "CoreIncludes.h"
    4443#include "TclBind.h"
     44#include "TclThreadList.h"
    4545
    4646namespace orxonox
    4747{
    48     const unsigned int TCLTHREADMANAGER_MAX_QUEUE_LENGTH = 100;
    4948    const float TCLTHREADMANAGER_MAX_CPU_USAGE = 0.50f;
    5049
     
    5554    SetConsoleCommand(TclThreadManager, execute, false).argumentCompleter(0, autocompletion::tclthreads());
    5655    SetConsoleCommand(TclThreadManager, query,   false).argumentCompleter(0, autocompletion::tclthreads());
    57     SetConsoleCommand(TclThreadManager, status,  false);
    58     SetConsoleCommand(TclThreadManager, dump,    false).argumentCompleter(0, autocompletion::tclthreads());
    59     SetConsoleCommand(TclThreadManager, flush,   false).argumentCompleter(0, autocompletion::tclthreads());
    60 
     56
     57    /**
     58        @brief A struct containing all informations about a Tcl-interpreter
     59    */
    6160    struct TclInterpreterBundle
    6261    {
    63         unsigned int id_;
    64 
    65         std::list<std::string> queue_;
    66         boost::mutex queueMutex_;
    67 
    68         Tcl::interpreter* interpreter_;
    69         std::string interpreterName_;
    70         boost::try_mutex interpreterMutex_;
    71 
    72         std::list<unsigned int> queriers_;
    73         boost::mutex queriersMutex_;
    74 
    75         bool running_;
    76         boost::mutex runningMutex_;
    77 
    78         bool finished_;
    79         boost::mutex finishedMutex_;
    80         boost::condition finishedCondition_;
     62        TclInterpreterBundle()
     63        {
     64            this->lock_ = new boost::unique_lock<boost::mutex>(this->mutex_, boost::defer_lock);
     65            this->bRunning_ = true;
     66        }
     67
     68        ~TclInterpreterBundle()
     69        {
     70            delete this->lock_;
     71        }
     72
     73        unsigned int                      id_;             ///< The id of the interpreter
     74        Tcl::interpreter*                 interpreter_;    ///< The Tcl-interpreter
     75        boost::mutex                      mutex_;          ///< A mutex to lock the interpreter while it's being used
     76        boost::unique_lock<boost::mutex>* lock_;           ///< The corresponding lock for the mutex
     77        TclThreadList<std::string>        queue_;          ///< The command queue for commands passed by execute(command)
     78        TclThreadList<unsigned int>       queriers_;       ///< A list containing the id's of all other threads sending a query to this interpreter (to avoid circular queries and deadlocks)
     79        bool                              bRunning_;       ///< This variable stays true until destroy() gets called
    8180    };
    8281
    83 
    84     static boost::thread::id threadID_g;
    85     static boost::mutex bundlesMutex_g;
    86     static boost::condition fullQueueCondition_g;
    87     static boost::condition orxonoxEvalCondition_g;
    88 
    89     TclThreadManager* TclThreadManager::singletonRef_s = 0;
    90 
     82    TclThreadManager* TclThreadManager::singletonPtr_s = 0;
     83
     84    /**
     85        @brief Constructor
     86        @param interpreter A pointer to the standard Tcl-interpreter (see @ref TclBind)
     87    */
    9188    TclThreadManager::TclThreadManager(Tcl::interpreter* interpreter)
    92         : orxonoxInterpreterBundle_(new TclInterpreterBundle())
    9389    {
    9490        RegisterRootObject(TclThreadManager);
    9591
    96         assert(singletonRef_s == 0);
    97         singletonRef_s = this;
    98 
    99         this->threadCounter_ = 0;
    100         this->orxonoxInterpreterBundle_->id_ = 0;
    101         this->orxonoxInterpreterBundle_->interpreter_ = interpreter;
    102         threadID_g = boost::this_thread::get_id();
    103     }
    104 
     92        assert(TclThreadManager::singletonPtr_s == 0);
     93        TclThreadManager::singletonPtr_s = this;
     94
     95        this->numInterpreterBundles_ = 0;
     96
     97        this->interpreterBundlesMutex_ = new boost::shared_mutex();
     98        this->mainInterpreterMutex_ = new boost::mutex();
     99        this->messageQueue_ = new TclThreadList<std::string>();
     100
     101        TclInterpreterBundle* newbundle = new TclInterpreterBundle();
     102        newbundle->id_ = 0;
     103        newbundle->interpreter_ = interpreter;
     104        newbundle->lock_->lock();
     105
     106        {
     107            boost::unique_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_);
     108            this->interpreterBundles_[0] = newbundle;
     109        }
     110    }
     111
     112    /**
     113        @brief Destructor
     114    */
    105115    TclThreadManager::~TclThreadManager()
    106116    {
    107         unsigned int threadID;
    108         {
    109             boost::mutex::scoped_lock bundles_lock(bundlesMutex_g);
    110             if (this->interpreterBundles_.begin() == this->interpreterBundles_.end())
    111                 return;
    112             else
    113                 threadID = this->interpreterBundles_.begin()->first;
    114         }
    115         this->destroy(threadID);
    116 
    117         delete this->orxonoxInterpreterBundle_;
    118 
    119         singletonRef_s = 0;
    120     }
    121 
    122     unsigned int TclThreadManager::create()
    123     {
    124         boost::mutex::scoped_lock bundles_lock(bundlesMutex_g);
    125         TclThreadManager::getInstance().threadCounter_++;
    126         std::string name = multi_cast<std::string>(TclThreadManager::getInstance().threadCounter_);
    127 
    128         TclInterpreterBundle* bundle = new TclInterpreterBundle;
    129         bundle->id_ = TclThreadManager::getInstance().threadCounter_;
    130         bundle->interpreter_ = TclThreadManager::getInstance().createNewTclInterpreter(name);
    131         bundle->interpreterName_ = name;
    132         bundle->running_ = true;
    133         bundle->finished_ = true;
    134 
    135         TclThreadManager::getInstance().interpreterBundles_[TclThreadManager::getInstance().threadCounter_] = bundle;
    136         COUT(0) << "Created new Tcl-interpreter with ID " << TclThreadManager::getInstance().threadCounter_ << std::endl;
    137         return TclThreadManager::getInstance().threadCounter_;
    138     }
    139 
    140     unsigned int TclThreadManager::createID(unsigned int threadID)
    141     {
    142         unsigned int temp = TclThreadManager::getInstance().threadCounter_;
    143         TclThreadManager::getInstance().threadCounter_ = threadID - 1;
    144         TclThreadManager::create();
    145         TclThreadManager::getInstance().threadCounter_ = temp;
    146         return threadID;
    147     }
    148 
    149     void TclThreadManager::destroy(unsigned int threadID)
    150     {
    151         TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(threadID);
     117        TclThreadManager::singletonPtr_s = 0;
     118
     119        delete this->interpreterBundlesMutex_;
     120        delete this->mainInterpreterMutex_;
     121        delete this->messageQueue_;
     122    }
     123
     124    /**
     125        @brief The "main loop" of the TclThreadManager. Creates new threads if needed and handles queries and queued commands for the main interpreter.
     126    */
     127    void TclThreadManager::update(const Clock& time)
     128    {
     129        // Get the bundle of the main interpreter (0)
     130        TclInterpreterBundle* bundle = this->getInterpreterBundle(0);
    152131        if (bundle)
    153132        {
     133            // Unlock the mutex to allow other threads accessing the main interpreter
     134            bundle->lock_->unlock();
     135
     136            // Lock the main interpreter mutex once to synchronize with threads that want to query the main interpreter
    154137            {
    155                 boost::mutex::scoped_lock running_lock(bundle->runningMutex_);
    156                 bundle->running_ = false;
     138                boost::unique_lock<boost::mutex> lock(*this->mainInterpreterMutex_);
    157139            }
    158             while (true)
     140
     141            // Lock the mutex again to gain exclusive access to the interpreter for the rest of the mainloop
     142            bundle->lock_->lock();
     143
     144            // Execute commands in the queues of the threaded interpreters
    159145            {
     146                boost::shared_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_);
     147                for (std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = this->interpreterBundles_.begin(); it != this->interpreterBundles_.end(); ++it)
    160148                {
    161                     boost::mutex::scoped_lock finished_lock(bundle->finishedMutex_);
    162                     if (bundle->finished_)
     149                    if (it->first == 0)
     150                        continue; // We'll handle the default interpreter later (and without threads of course)
     151
     152                    TclInterpreterBundle* bundle = it->second;
     153                    if (!bundle->queue_.empty())
    163154                    {
    164                         boost::mutex::scoped_lock bundles_lock(bundlesMutex_g);
    165                         boost::mutex::scoped_try_lock interpreter_lock(bundle->interpreterMutex_);
     155                        // There are commands in the queue
    166156                        try
    167157                        {
    168                             while (!interpreter_lock.try_lock())
     158                            if (!bundle->lock_->owns_lock() && bundle->lock_->try_lock())
    169159                            {
    170                                 orxonoxEvalCondition_g.notify_one();
    171                                 boost::this_thread::yield();
     160                                // We sucessfully obtained a lock for the interpreter
     161                                std::string command;
     162                                if (bundle->queue_.try_pop_front(&command))
     163                                {
     164                                    // Start a thread to execute the command
     165                                    boost::thread(boost::bind(&tclThread, bundle, command));
     166                                }
     167                                else
     168                                {
     169                                    // Somehow the queue become empty (maybe multiple consumers) - unlock the mutex
     170                                    bundle->lock_->unlock();
     171                                }
    172172                            }
    173                         } catch (...) {}
    174                         delete bundle->interpreter_;
    175                         delete bundle;
    176                         TclThreadManager::getInstance().interpreterBundles_.erase(threadID);
    177                         break;
     173                        }
     174                        catch (...)
     175                        {
     176                            // A lock error occurred - this is possible if the lock gets locked between !bundle->lock_->owns_lock() and bundle->lock_->try_lock()
     177                            // This isn't too bad, just continue
     178                        }
    178179                    }
    179180                }
    180 
    181                 orxonoxEvalCondition_g.notify_one();
    182                 boost::this_thread::yield();
    183181            }
    184182
    185             COUT(0) << "Destroyed Tcl-interpreter with ID " << threadID << std::endl;
    186         }
    187     }
    188 
    189     void TclThreadManager::execute(unsigned int threadID, const std::string& _command)
    190     {
    191         std::string command = stripEnclosingBraces(_command);
    192 
    193         if (threadID == 0)
    194             TclThreadManager::getInstance().pushCommandToQueue(command);
    195         else
    196             TclThreadManager::getInstance().pushCommandToQueue(threadID, command);
    197     }
    198 
    199     std::string TclThreadManager::query(unsigned int threadID, const std::string& command)
    200     {
    201         return TclThreadManager::getInstance().evalQuery(TclThreadManager::getInstance().orxonoxInterpreterBundle_->id_, threadID, command);
    202     }
    203 
    204     void TclThreadManager::status()
    205     {
    206         COUT(0) << "Thread ID" << '\t' << "Queue size" << '\t' << "State" << std::endl;
    207 
    208         std::string output = "Orxonox";
    209         output += "\t\t";
    210         {
    211             boost::mutex::scoped_lock queue_lock(TclThreadManager::getInstance().orxonoxInterpreterBundle_->queueMutex_);
    212             output += multi_cast<std::string>(TclThreadManager::getInstance().orxonoxInterpreterBundle_->queue_.size());
    213         }
    214         output += "\t\t";
    215         output += "busy";
    216         COUT(0) << output << std::endl;
    217 
    218         boost::mutex::scoped_lock bundles_lock(bundlesMutex_g);
    219         for (std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = TclThreadManager::getInstance().interpreterBundles_.begin(); it != TclThreadManager::getInstance().interpreterBundles_.end(); ++it)
    220         {
    221             std::string output = multi_cast<std::string>((*it).first);
    222             output += "\t\t";
     183            // Execute commands in the message queue
     184            if (!this->messageQueue_->empty())
    223185            {
    224                 boost::mutex::scoped_lock queue_lock((*it).second->queueMutex_);
    225                 output += multi_cast<std::string>((*it).second->queue_.size());
     186                std::string command;
     187                while (true)
     188                {
     189                    // Pop the front value from the list (break the loop if there are no elements in the list)
     190                    if (!this->messageQueue_->try_pop_front(&command))
     191                        break;
     192
     193                    // Execute the command
     194                    CommandExecutor::execute(command, false);
     195                }
    226196            }
    227             output += "\t\t";
     197
     198            // Execute commands in the queue of the main interpreter
     199            if (!bundle->queue_.empty())
    228200            {
    229                 boost::mutex::scoped_try_lock interpreter_lock((*it).second->interpreterMutex_);
    230                 if (interpreter_lock.try_lock())
    231                     output += "ready";
    232                 else
    233                     output += "busy";
     201                // Calculate the time we have until we reach the maximal cpu usage
     202                unsigned long maxtime = (unsigned long)(time.getDeltaTime() * 1000000 * TCLTHREADMANAGER_MAX_CPU_USAGE);
     203
     204                Ogre::Timer timer;
     205                std::string command;
     206
     207                while (timer.getMicroseconds() < maxtime)
     208                {
     209                    // Pop the front value from the list (break the loop if there are no elements in the list)
     210                    if (!bundle->queue_.try_pop_front(&command))
     211                        break;
     212
     213                    // Execute the command
     214                    CommandExecutor::execute(command, false);
     215                }
    234216            }
    235             COUT(0) << output << std::endl;
    236         }
    237     }
    238 
    239     void TclThreadManager::dump(unsigned int threadID)
    240     {
    241         TclInterpreterBundle* bundle = 0;
    242         if (threadID == 0)
    243         {
    244             bundle = TclThreadManager::getInstance().orxonoxInterpreterBundle_;
    245             COUT(0) << "Queue dump of Orxonox:" << std::endl;
    246         }
    247         else
    248         {
    249             if ((bundle = TclThreadManager::getInstance().getInterpreterBundle(threadID)))
     217        }
     218    }
     219
     220    /**
     221        @brief Creates a new Tcl-interpreter.
     222    */
     223    unsigned int TclThreadManager::create()
     224    {
     225        TclThreadManager::getInstance().numInterpreterBundles_++;
     226        TclThreadManager::createWithId(TclThreadManager::getInstance().numInterpreterBundles_);
     227        COUT(0) << "Created new Tcl-interpreter with ID " << TclThreadManager::getInstance().numInterpreterBundles_ << std::endl;
     228        return TclThreadManager::getInstance().numInterpreterBundles_;
     229    }
     230
     231    /**
     232        @brief Creates a new Tcl-interpreter with a given id.
     233
     234        Use with caution - if the id collides with an already existing interpreter, this call will fail.
     235        This will also be a problem, if the auto-numbered interpreters (by using create()) reach an id
     236        which was previously used in this function. Use high numbers to be safe.
     237    */
     238    Tcl::interpreter* TclThreadManager::createWithId(unsigned int id)
     239    {
     240        TclInterpreterBundle* newbundle = new TclInterpreterBundle();
     241        newbundle->id_ = id;
     242        newbundle->interpreter_ = new Tcl::interpreter(TclBind::getInstance().getTclLibPath());
     243
     244        // Initialize the new interpreter
     245        try
     246        {
     247            std::string id_string = getConvertedValue<unsigned int, std::string>(id);
     248
     249            // Define the functions which are implemented in C++
     250            newbundle->interpreter_->def("orxonox::execute",      TclThreadManager::tcl_execute,      Tcl::variadic());
     251            newbundle->interpreter_->def("orxonox::crossexecute", TclThreadManager::tcl_crossexecute, Tcl::variadic());
     252            newbundle->interpreter_->def("orxonox::query",        TclThreadManager::tcl_query,        Tcl::variadic());
     253            newbundle->interpreter_->def("orxonox::crossquery",   TclThreadManager::tcl_crossquery,   Tcl::variadic());
     254            newbundle->interpreter_->def("orxonox::running",      TclThreadManager::tcl_running);
     255
     256            // Create threadspecific shortcuts for the functions above
     257            newbundle->interpreter_->def("execute",      TclThreadManager::tcl_execute,      Tcl::variadic());
     258            newbundle->interpreter_->def("crossexecute", TclThreadManager::tcl_crossexecute, Tcl::variadic());
     259            newbundle->interpreter_->eval("proc query       args     { orxonox::query " + id_string + " $args }");
     260            newbundle->interpreter_->eval("proc crossquery {id args} { orxonox::crossquery " + id_string + " $id $args }");
     261
     262            // Define a variable containing the thread id
     263            newbundle->interpreter_->eval("set id " + id_string);
     264
     265            // Use our own exit function to avoid shutting down the whole program instead of just the interpreter
     266            newbundle->interpreter_->eval("rename exit tcl::exit");
     267            newbundle->interpreter_->eval("proc exit {} { execute TclThreadManager destroy " + id_string + " }");
     268
     269            // Redefine some native functions
     270            newbundle->interpreter_->eval("redef_puts");
     271
     272//            newbundle->interpreter_->eval("rename while tcl::while");
     273//            newbundle->interpreter_->eval("proc while {test command} { tcl::while {[uplevel 1 expr $test]} {uplevel 1 $command} }"); // (\"$test\" && [orxonox::running " + id + "]])
     274//            newbundle->interpreter_->eval("rename for tcl::for");
     275//            newbundle->interpreter_->eval("proc for {start test next command} { uplevel tcl::for \"$start\" \"$test\" \"$next\" \"$command\" }");
     276        }
     277        catch (const Tcl::tcl_error& e)
     278        {   newbundle->interpreter_ = 0; COUT(1) << "Tcl error while creating Tcl-interpreter (" << id << "): " << e.what() << std::endl;   }
     279        catch (const std::exception& e)
     280        {   newbundle->interpreter_ = 0; COUT(1) << "Error while creating Tcl-interpreter (" << id << "): " << e.what() << std::endl;   }
     281        catch (...)
     282        {   newbundle->interpreter_ = 0; COUT(1) << "An error occurred while creating a new Tcl-interpreter (" << id << ")" << std::endl;   }
     283
     284        {
     285            // Add the new bundle to the map
     286            boost::unique_lock<boost::shared_mutex> lock(*TclThreadManager::getInstance().interpreterBundlesMutex_);
     287            TclThreadManager::getInstance().interpreterBundles_[id] = newbundle;
     288        }
     289
     290        return newbundle->interpreter_;
     291    }
     292
     293    /**
     294        @brief Stops and destroys a given Tcl-interpreter
     295    */
     296    void TclThreadManager::destroy(unsigned int id)
     297    {
     298        // TODO
     299        // Not yet implemented
     300    }
     301
     302    /**
     303        @brief Sends a command to the queue of a given Tcl-interpreter
     304        @param id The id of the target interpreter
     305        @param command The command to be sent
     306    */
     307    void TclThreadManager::execute(unsigned int target_id, const std::string& command)
     308    {
     309        TclThreadManager::getInstance()._execute(target_id, command);
     310    }
     311
     312    /**
     313        @brief This function can be called from Tcl to execute a console command.
     314
     315        Commands which shall be executed are put into a queue and processed as soon as the
     316        main thread feels ready to do so. The queue may also be full which results in a temporary
     317        suspension of the calling thread until the queue gets ready again.
     318    */
     319    void TclThreadManager::tcl_execute(const Tcl::object& args)
     320    {
     321        TclThreadManager::getInstance()._execute(0, stripEnclosingBraces(args.get()));
     322    }
     323
     324    /**
     325        @brief This function can be called from Tcl to send a command to the queue of any interpreter.
     326        @param target_id The id of the target thread
     327    */
     328    void TclThreadManager::tcl_crossexecute(int target_id, const Tcl::object& args)
     329    {
     330        TclThreadManager::getInstance()._execute(static_cast<unsigned int>(target_id), stripEnclosingBraces(args.get()));
     331    }
     332
     333    /**
     334        @brief Sends a command to the queue of a given Tcl-interpreter
     335        @param id The id of the target interpreter
     336        @param command The command to be sent
     337    */
     338    void TclThreadManager::_execute(unsigned int target_id, const std::string& command)
     339    {
     340        TclInterpreterBundle* bundle = this->getInterpreterBundle(target_id);
     341        if (bundle)
     342            bundle->queue_.push_back(command);
     343    }
     344
     345
     346    /**
     347        @brief Sends a query to a given Tcl-interpreter and waits for the result
     348        @param id The id of the target interpreter
     349        @param command The command to be sent
     350        @return The result of the command
     351    */
     352    std::string TclThreadManager::query(unsigned int target_id, const std::string& command)
     353    {
     354        return TclThreadManager::getInstance()._query(0, target_id, command);
     355    }
     356
     357    /**
     358        @brief This function can be called from Tcl to send a query to the main thread.
     359        @param source_id The id of the calling thread
     360
     361        A query waits for the result of the command. This means, the calling thread will be blocked until
     362        the main thread answers the query. In return, the main thread sends the result of the console
     363        command back to Tcl.
     364    */
     365    std::string TclThreadManager::tcl_query(int source_id, const Tcl::object& args)
     366    {
     367        return TclThreadManager::getInstance()._query(static_cast<unsigned int>(source_id), 0, stripEnclosingBraces(args.get()), true);
     368    }
     369
     370    /**
     371        @brief This function can be called from Tcl to send a query to another thread.
     372        @param source_id The id of the calling thread
     373        @param target_id The id of the target thread
     374    */
     375    std::string TclThreadManager::tcl_crossquery(int source_id, int target_id, const Tcl::object& args)
     376    {
     377        return TclThreadManager::getInstance()._query(static_cast<unsigned int>(source_id), static_cast<unsigned int>(target_id), stripEnclosingBraces(args.get()));
     378    }
     379
     380    /**
     381        @brief This function performs a query to any Tcl interpreter
     382        @param source_id The id of the calling thread
     383        @param target_id The id of the target thread
     384        @param command The command to send as a query
     385        @param bUseCommandExecutor Only used if the target_id is 0 (which references the main interpreter). In this case it means if the command should be passed to the CommandExecutor (true) or to the main Tcl interpreter (false). This is true when called by tcl_query and false when called by tcl_crossquery.
     386    */
     387    std::string TclThreadManager::_query(unsigned int source_id, unsigned int target_id, const std::string& command, bool bUseCommandExecutor)
     388    {
     389        TclInterpreterBundle* source_bundle = this->getInterpreterBundle(source_id);
     390        TclInterpreterBundle* target_bundle = this->getInterpreterBundle(target_id);
     391        std::string output;
     392
     393        if (source_bundle && target_bundle)
     394        {
     395            // At this point we assume the mutex of source_bundle to be locked (because it's executing this query right now an waits for the return value)
     396            // We can safely use it's querier list (because there's no other place in the code using the list except this query - and the interpreter can't start more than one query)
     397
     398            if ((source_bundle->id_ == target_bundle->id_) || source_bundle->queriers_.is_in(target_bundle->id_))
    250399            {
    251                 COUT(0) << "Queue dump of Tcl-thread " << threadID << ":" << std::endl;
     400                // This query would lead to a deadlock - return with an error
     401                this->error("Error: Circular query (" + this->dumpList(source_bundle->queriers_.getList()) + " " + getConvertedValue<unsigned int, std::string>(source_bundle->id_) \
     402                            + " -> " + getConvertedValue<unsigned int, std::string>(target_bundle->id_) \
     403                            + "), couldn't query Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(target_bundle->id_) \
     404                            + " from other interpreter with ID " + getConvertedValue<unsigned int, std::string>(source_bundle->id_) + ".");
    252405            }
    253406            else
    254                 return;
    255         }
    256 
    257         boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
    258         unsigned int index = 0;
    259         for (std::list<std::string>::const_iterator it = bundle->queue_.begin(); it != bundle->queue_.end(); ++it)
    260         {
    261             index++;
    262             COUT(0) << index << ": " << (*it) << std::endl;
    263         }
    264     }
    265 
    266     void TclThreadManager::flush(unsigned int threadID)
    267     {
    268         TclInterpreterBundle* bundle = 0;
    269         if (threadID == 0)
    270             bundle = TclThreadManager::getInstance().orxonoxInterpreterBundle_;
    271         else
    272             if (!(bundle = TclThreadManager::getInstance().getInterpreterBundle(threadID)))
    273                 return;
    274 
    275         boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
    276         bundle->queue_.clear();
    277         if (threadID == 0)
    278         {
    279             COUT(0) << "Flushed queue of Orxonox Tcl-interpreter." << std::endl;
    280         }
    281         else
    282         {
    283             COUT(0) << "Flushed queue of Tcl-interpreter " << threadID << "." << std::endl;
    284         }
    285     }
    286 
    287     void TclThreadManager::tcl_execute(Tcl::object const &args)
    288     {
    289         TclThreadManager::getInstance().pushCommandToQueue(stripEnclosingBraces(args.get()));
    290     }
    291 
    292     std::string TclThreadManager::tcl_query(int querierID, Tcl::object const &args)
    293     {
    294         return TclThreadManager::getInstance().evalQuery(static_cast<unsigned int>(querierID), stripEnclosingBraces(args.get()));
    295     }
    296 
    297     std::string TclThreadManager::tcl_crossquery(int querierID, int threadID, Tcl::object const &args)
    298     {
    299         return TclThreadManager::getInstance().evalQuery(static_cast<unsigned int>(querierID), static_cast<unsigned int>(threadID), stripEnclosingBraces(args.get()));
    300     }
    301 
    302     bool TclThreadManager::tcl_running(int threadID)
    303     {
    304         TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(static_cast<unsigned int>(threadID));
    305         if (bundle)
    306         {
    307             boost::mutex::scoped_lock running_lock(bundle->runningMutex_);
    308             return bundle->running_;
    309         }
    310         return false;
    311     }
    312 
    313     Tcl::interpreter* TclThreadManager::createNewTclInterpreter(const std::string& threadID)
    314     {
    315         Tcl::interpreter* i = 0;
    316         i = new Tcl::interpreter(TclBind::getInstance().getTclLibPath());
    317 
    318         try
    319         {
    320             i->def("orxonox::query", TclThreadManager::tcl_query, Tcl::variadic());
    321             i->def("orxonox::crossquery", TclThreadManager::tcl_crossquery, Tcl::variadic());
    322             i->def("orxonox::execute", TclThreadManager::tcl_execute, Tcl::variadic());
    323             i->def("orxonox::running", TclThreadManager::tcl_running);
    324 
    325             i->def("execute", TclThreadManager::tcl_execute, Tcl::variadic());
    326             i->eval("proc query args { orxonox::query " + threadID + " $args }");
    327             i->eval("proc crossquery {id args} { orxonox::crossquery " + threadID + " $id $args }");
    328             i->eval("set id " + threadID);
    329 
    330             i->eval("rename exit tcl::exit");
    331             i->eval("proc exit {} { orxonox TclThreadManager destroy " + threadID + " }");
    332 
    333             i->eval("redef_puts");
    334 
    335 //            i->eval("rename while tcl::while");
    336 //            i->eval("proc while {test command} { tcl::while {[uplevel 1 expr $test]} {uplevel 1 $command} }"); // (\"$test\" && [orxonox::running " + threadID + "]])
    337 //            i->eval("rename for tcl::for");
    338 //            i->eval("proc for {start test next command} { uplevel tcl::for \"$start\" \"$test\" \"$next\" \"$command\" }");
    339         }
    340         catch (Tcl::tcl_error const &e)
    341         {   COUT(1) << "Tcl error while creating Tcl-interpreter (" << threadID << "): " << e.what() << std::endl;   }
    342         catch (std::exception const &e)
    343         {   COUT(1) << "Error while creating Tcl-interpreter (" << threadID << "): " << e.what() << std::endl;   }
    344 
    345         return i;
    346     }
    347 
    348     TclInterpreterBundle* TclThreadManager::getInterpreterBundle(unsigned int threadID)
    349     {
    350         boost::mutex::scoped_lock bundles_lock(bundlesMutex_g);
    351         std::map<unsigned int, TclInterpreterBundle*>::iterator it = this->interpreterBundles_.find(threadID);
    352         if (it != this->interpreterBundles_.end())
    353         {
    354             return (*it).second;
    355         }
    356         else
    357         {
    358             this->error("Error: No Tcl-interpreter with ID " + multi_cast<std::string>(threadID) + " existing.");
    359             return 0;
    360         }
    361     }
    362 
    363     Tcl::interpreter* TclThreadManager::getTclInterpreter(unsigned int threadID)
    364     {
    365         return this->getInterpreterBundle(threadID)->interpreter_;
    366     }
    367 
    368     std::string TclThreadManager::dumpList(const std::list<unsigned int>& list)
    369     {
    370         std::string output = "";
    371         for (std::list<unsigned int>::const_iterator it = list.begin(); it != list.end(); ++it)
    372         {
    373             if (it != list.begin())
    374                 output += " ";
    375 
    376             output += multi_cast<std::string>(*it);
    377         }
    378         return output;
    379     }
    380 
    381     void TclThreadManager::error(const std::string& error)
    382     {
    383         if (boost::this_thread::get_id() != threadID_g)
    384         {
    385             boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_->queueMutex_);
    386             if (this->orxonoxInterpreterBundle_->queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
    387407            {
    388                 boost::this_thread::yield();
    389                 return;
    390             }
    391         }
    392 
    393         this->forceCommandToFrontOfQueue("error " + error);
    394     }
    395 
    396     void TclThreadManager::debug(const std::string& error)
    397     {
    398         if (boost::this_thread::get_id() != threadID_g)
    399         {
    400             boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_->queueMutex_);
    401             if (this->orxonoxInterpreterBundle_->queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
    402             {
    403                 boost::this_thread::yield();
    404                 return;
    405             }
    406         }
    407 
    408         this->forceCommandToFrontOfQueue("debug " + error);
    409     }
    410 
    411     void TclThreadManager::pushCommandToQueue(const std::string& command)
    412     {
    413         boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_->queueMutex_);
    414         while (this->orxonoxInterpreterBundle_->queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
    415             fullQueueCondition_g.wait(queue_lock);
    416 
    417         this->orxonoxInterpreterBundle_->queue_.push_back(command);
    418     }
    419 
    420     void TclThreadManager::forceCommandToFrontOfQueue(const std::string& command)
    421     {
    422         boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_->queueMutex_);
    423         this->orxonoxInterpreterBundle_->queue_.push_front(command);
    424     }
    425 
    426     std::string TclThreadManager::popCommandFromQueue()
    427     {
    428         boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_->queueMutex_);
    429         std::string temp = this->orxonoxInterpreterBundle_->queue_.front();
    430         this->orxonoxInterpreterBundle_->queue_.pop_front();
    431         fullQueueCondition_g.notify_one();
    432         return temp;
    433     }
    434 
    435     bool TclThreadManager::queueIsEmpty()
    436     {
    437         boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_->queueMutex_);
    438         return this->orxonoxInterpreterBundle_->queue_.empty();
    439     }
    440 
    441     void TclThreadManager::pushCommandToQueue(unsigned int threadID, const std::string& command)
    442     {
    443         TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
    444         if (bundle)
    445         {
    446             boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
    447             if (bundle->queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
    448             {
    449                 this->error("Error: Queue of Tcl-interpreter " + multi_cast<std::string>(threadID) + " is full, couldn't add command.");
    450                 return;
    451             }
    452 
    453             bundle->queue_.push_back(command);
    454         }
    455     }
    456 
    457     std::string TclThreadManager::popCommandFromQueue(unsigned int threadID)
    458     {
    459         TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
    460         if (bundle)
    461         {
    462             boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
    463             std::string temp = bundle->queue_.front();
    464             bundle->queue_.pop_front();
    465             return temp;
    466         }
    467         return "";
    468     }
    469 
    470     bool TclThreadManager::queueIsEmpty(unsigned int threadID)
    471     {
    472         TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
    473         if (bundle)
    474         {
    475             boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
    476             return bundle->queue_.empty();
    477         }
    478         return true;
    479     }
    480 
    481     bool TclThreadManager::updateQueriersList(TclInterpreterBundle* querier, TclInterpreterBundle* target)
    482     {
    483         if (querier == target)
    484             return false;
    485 
    486         boost::mutex::scoped_lock queriers_lock(target->queriersMutex_);
    487 
    488         {
    489             boost::mutex::scoped_lock queriers_lock(querier->queriersMutex_);
    490             target->queriers_.insert(target->queriers_.end(), querier->queriers_.begin(), querier->queriers_.end());
    491         }
    492 
    493         target->queriers_.insert(target->queriers_.end(), querier->id_);
    494 
    495         if (std::find(target->queriers_.begin(), target->queriers_.end(), target->id_) != target->queriers_.end())
    496         {
    497             this->error("Error: Circular query (" + this->dumpList(target->queriers_) + " -> " + multi_cast<std::string>(target->id_) + "), couldn't query Tcl-interpreter with ID " + multi_cast<std::string>(target->id_) + " from other interpreter with ID " + multi_cast<std::string>(querier->id_) + ".");
    498             return false;
    499         }
    500 
    501         return true;
    502     }
    503 
    504     std::string TclThreadManager::evalQuery(unsigned int querierID, const std::string& command)
    505     {
    506         TclInterpreterBundle* querier = this->getInterpreterBundle(querierID);
    507         std::string output = "";
    508         if (querier)
    509         {
    510             if (this->updateQueriersList(querier, this->orxonoxInterpreterBundle_))
    511             {
    512                 boost::mutex::scoped_lock interpreter_lock(this->orxonoxInterpreterBundle_->interpreterMutex_);
    513                 orxonoxEvalCondition_g.wait(interpreter_lock);
    514 
    515                 if (!CommandExecutor::execute(command, false))
    516                     this->error("Error: Can't execute command \"" + command + "\"!");
    517 
    518                 if (CommandExecutor::getLastEvaluation().hasReturnvalue())
    519                     output = CommandExecutor::getLastEvaluation().getReturnvalue().getString();
    520             }
    521 
    522             boost::mutex::scoped_lock queriers_lock(this->orxonoxInterpreterBundle_->queriersMutex_);
    523             this->orxonoxInterpreterBundle_->queriers_.clear();
    524         }
    525         return output;
    526     }
    527 
    528     std::string TclThreadManager::evalQuery(unsigned int querierID, unsigned int threadID, const std::string& command)
    529     {
    530         TclInterpreterBundle* target = 0;
    531         if (threadID)
    532             target = this->getInterpreterBundle(threadID);
    533         else
    534             target = this->orxonoxInterpreterBundle_;
    535 
    536         std::string output = "";
    537         if (target)
    538         {
    539             TclInterpreterBundle* querier = 0;
    540             if (querierID)
    541                 querier = this->getInterpreterBundle(querierID);
    542             else
    543                 querier = this->orxonoxInterpreterBundle_;
    544 
    545             if (querier)
    546             {
    547                 if (this->updateQueriersList(querier, target))
     408                boost::unique_lock<boost::mutex> lock(target_bundle->mutex_, boost::try_to_lock);
     409                boost::unique_lock<boost::mutex> mainlock(*this->mainInterpreterMutex_, boost::defer_lock);
     410
     411                if (!lock.owns_lock() && source_bundle->id_ != 0)
    548412                {
    549                     boost::mutex::scoped_try_lock interpreter_lock(target->interpreterMutex_);
    550                     bool successfullyLocked = false;
    551                     try
     413                    // We couldn't obtain the try_lock immediately and we're not the main interpreter - wait until the lock becomes possible (note: the main interpreter won't wait and instead returns an error - see below)
     414                    if (target_bundle->id_ == 0)
    552415                    {
    553                         if (querierID == 0 || std::find(querier->queriers_.begin(), querier->queriers_.end(), 0U) != querier->queriers_.end())
    554                             successfullyLocked = interpreter_lock.try_lock();
    555                         else
    556                         {
    557                             while (!interpreter_lock.try_lock())
    558                             {
    559                                 boost::this_thread::yield();
    560                             }
    561 
    562                             successfullyLocked = true;
    563                         }
    564                     } catch (...) {}
    565 
    566                     if (successfullyLocked)
    567                     {
    568                         this->debug("TclThread_query: " + command);
    569                         try
    570                         {   output = static_cast<std::string>(target->interpreter_->eval(command));   }
    571                         catch (Tcl::tcl_error const &e)
    572                         {   this->error("Tcl error: " + static_cast<std::string>(e.what()));   }
    573                         catch (std::exception const &e)
    574                         {   this->error("Error while executing Tcl: " + static_cast<std::string>(e.what()));   }
     416                        // We're querying the main interpreter - use the main interpreter mutex to synchronize
     417                        mainlock.lock();
     418                        lock.lock();
    575419                    }
    576420                    else
    577421                    {
    578                         this->error("Error: Couldn't query Tcl-interpreter with ID " + multi_cast<std::string>(threadID) + ", interpreter is busy right now.");
     422                        // We're querying a threaded interpreter - no synchronization needed
     423                        lock.lock();
    579424                    }
    580425                }
    581426
    582                 boost::mutex::scoped_lock queriers_lock(target->queriersMutex_);
    583                 target->queriers_.clear();
    584             }
    585         }
    586         return output;
    587     }
    588 
    589     void TclThreadManager::update(const Clock& time)
    590     {
    591         {
    592             orxonoxEvalCondition_g.notify_one();
    593             boost::this_thread::yield();
    594         }
    595 
    596         {
    597             boost::mutex::scoped_lock bundles_lock(bundlesMutex_g);
    598             for (std::map<unsigned int, TclInterpreterBundle*>::iterator it = this->interpreterBundles_.begin(); it != this->interpreterBundles_.end(); ++it)
    599             {
    600                 boost::mutex::scoped_lock queue_lock((*it).second->queueMutex_);
    601                 if (!(*it).second->queue_.empty())
     427                if (lock.owns_lock())
    602428                {
    603                     std::string command = (*it).second->queue_.front();
    604                     (*it).second->queue_.pop_front();
     429                    // Now the mutex of target_bundle is also locked an we can update the querier list
     430                    target_bundle->queriers_.insert(target_bundle->queriers_.getList().begin(), source_bundle->queriers_.getList().begin(), source_bundle->queriers_.getList().end());
     431                    target_bundle->queriers_.push_back(source_bundle->id_);
     432
     433                    // Perform the query (note: this happens in the main thread because we need the returnvalue)
     434                    if (target_bundle->id_ == 0 && bUseCommandExecutor)
    605435                    {
    606                         boost::mutex::scoped_lock finished_lock((*it).second->finishedMutex_);
    607                         (*it).second->finished_ = false;
     436                        // It's a query to the CommandExecutor
     437                        this->debug("TclThread_query -> CE: " + command);
     438                        if (!CommandExecutor::execute(command, false))
     439                            this->error("Error: Can't execute command \"" + command + "\"!");
     440
     441                        if (CommandExecutor::getLastEvaluation().hasReturnvalue())
     442                            output = CommandExecutor::getLastEvaluation().getReturnvalue().getString();
    608443                    }
    609                     boost::thread(boost::bind(&tclThread, (*it).second, command));
     444                    else
     445                    {
     446                        // It's a query to a Tcl interpreter
     447                        this->debug("TclThread_query: " + command);
     448
     449                        output = this->eval(target_bundle, command);
     450                    }
     451
     452                    // Clear the queriers list of the target
     453                    target_bundle->queriers_.clear();
     454
     455                    // Unlock the mutex of the target_bundle
     456                    lock.unlock();
     457
     458                    // Finally unlock the main interpreter lock if necessary
     459                    if (mainlock.owns_lock())
     460                        mainlock.unlock();
     461                }
     462                else
     463                {
     464                    // This happens if the main thread tries to query a busy interpreter
     465                    // To avoid a lock of the main thread, we simply don't proceed with the query in this case
     466                    this->error("Error: Couldn't query Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(target_bundle->id_) + ", interpreter is busy right now.");
    610467                }
    611468            }
    612         }
    613 
    614         {
    615             boost::mutex::scoped_lock interpreter_lock(this->orxonoxInterpreterBundle_->interpreterMutex_);
    616             unsigned long maxtime = static_cast<unsigned long>(time.getDeltaTime() * 1000000 * TCLTHREADMANAGER_MAX_CPU_USAGE);
    617             Ogre::Timer timer;
    618             while (!this->queueIsEmpty())
    619             {
    620                 CommandExecutor::execute(this->popCommandFromQueue(), false);
    621                 if (timer.getMicroseconds() > maxtime)
    622                     break;
    623             }
    624         }
    625     }
    626 
     469
     470        }
     471
     472        return output;
     473    }
     474
     475    /**
     476        @brief This function can be called from Tcl to ask if the thread is still suposed to be running.
     477        @param id The id of the thread in question
     478    */
     479    bool TclThreadManager::tcl_running(int id)
     480    {
     481        TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(static_cast<unsigned int>(id));
     482        if (bundle)
     483            return bundle->bRunning_;
     484        else
     485            return false;
     486    }
     487
     488    /**
     489        @brief Returns the interpreter bundle with the given id.
     490        @param id The id of the interpreter
     491        @return The interpreter or 0 if the id doesn't exist
     492    */
     493    TclInterpreterBundle* TclThreadManager::getInterpreterBundle(unsigned int id)
     494    {
     495        boost::shared_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_);
     496
     497        std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = this->interpreterBundles_.find(id);
     498        if (it != this->interpreterBundles_.end())
     499        {
     500            return it->second;
     501        }
     502        else
     503        {
     504            this->error("Error: No Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(id) + " existing.");
     505            return 0;
     506        }
     507    }
     508
     509    /**
     510        @brief Returns a string containing all elements of a unsigned-integer-list separated by spaces.
     511    */
     512    std::string TclThreadManager::dumpList(const std::list<unsigned int>& list)
     513    {
     514        std::string output = "";
     515        for (std::list<unsigned int>::const_iterator it = list.begin(); it != list.end(); ++it)
     516        {
     517            if (it != list.begin())
     518                output += " ";
     519
     520            output += getConvertedValue<unsigned int, std::string>(*it);
     521        }
     522        return output;
     523    }
     524
     525    /**
     526        @brief Returns a list with the numbers of all existing Tcl-interpreters.
     527
     528        This function is used by the auto completion function.
     529    */
    627530    std::list<unsigned int> TclThreadManager::getThreadList() const
    628531    {
    629         boost::mutex::scoped_lock bundles_lock(bundlesMutex_g);
     532        boost::shared_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_);
     533
    630534        std::list<unsigned int> threads;
    631535        for (std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = this->interpreterBundles_.begin(); it != this->interpreterBundles_.end(); ++it)
    632             threads.push_back((*it).first);
     536            if (it->first > 0 && it->first <= this->numInterpreterBundles_) // only list autonumbered interpreters (created with create()) - exclude the default interpreter 0 and all manually numbered interpreters)
     537                threads.push_back(it->first);
    633538        return threads;
    634539    }
    635540
    636     void tclThread(TclInterpreterBundle* interpreterBundle, std::string command)
     541    /**
     542        @brief A helper function to print errors in a thread safe manner.
     543    */
     544    void TclThreadManager::error(const std::string& error)
     545    {
     546        this->messageQueue_->push_back("error " + error);
     547    }
     548
     549    /**
     550        @brief A helper function to print debug information in a thread safe manner.
     551    */
     552    void TclThreadManager::debug(const std::string& error)
     553    {
     554        this->messageQueue_->push_back("debug " + error);
     555    }
     556
     557    /**
     558        @brief Evaluates a Tcl command without throwing exceptions (which may rise problems on certain machines).
     559        @return The Tcl return value
     560
     561        Errors are reported through the @ref error function.
     562    */
     563    std::string TclThreadManager::eval(TclInterpreterBundle* bundle, const std::string& command)
     564    {
     565        Tcl_Interp* interpreter = bundle->interpreter_->get();
     566        int cc = Tcl_Eval(interpreter, command.c_str());
     567
     568        Tcl::details::result result(interpreter);
     569
     570        if (cc != TCL_OK)
     571        {
     572            this->error("Tcl error (execute, ID " + getConvertedValue<unsigned int, std::string>(bundle->id_) + "): " + static_cast<std::string>(result));
     573            return "";
     574        }
     575        else
     576        {
     577            return result;
     578        }
     579    }
     580
     581    ////////////////
     582    // The Thread //
     583    ////////////////
     584
     585    /**
     586        @brief The main function of the thread. Executes a Tcl command.
     587        @param bundle The interpreter bundle containing all necessary variables
     588        @param command the Command to execute
     589    */
     590    void tclThread(TclInterpreterBundle* bundle, std::string command)
    637591    {
    638592        TclThreadManager::getInstance().debug("TclThread_execute: " + command);
    639         boost::mutex::scoped_lock interpreter_lock(interpreterBundle->interpreterMutex_);
    640         try
    641         {
    642             interpreterBundle->interpreter_->eval(command);
    643         }
    644         catch (Tcl::tcl_error const &e)
    645         {
    646             TclThreadManager::getInstance().error("Tcl (ID " + multi_cast<std::string>(interpreterBundle->id_) + ") error: " + e.what());
    647         }
    648         catch (std::exception const &e)
    649         {
    650             TclThreadManager::getInstance().error("Error while executing Tcl (ID " + multi_cast<std::string>(interpreterBundle->id_) + "): " + e.what());
    651         }
    652 
    653         boost::mutex::scoped_lock finished_lock(interpreterBundle->finishedMutex_);
    654         interpreterBundle->finished_ = true;
    655         interpreterBundle->finishedCondition_.notify_all();
     593
     594        TclThreadManager::getInstance().eval(bundle, command);
     595
     596        bundle->lock_->unlock();
    656597    }
    657598}
  • code/trunk/src/core/TclThreadManager.h

    r3304 r3307  
    3333
    3434#include <cassert>
    35 #include <list>
    3635#include <map>
    3736#include <string>
    38 #include "core/OrxonoxClass.h"
     37
     38#include "OrxonoxClass.h"
     39
     40namespace boost
     41{
     42    // forward declarations
     43    class mutex;
     44    class shared_mutex;
     45    class condition_variable;
     46}
    3947
    4048namespace orxonox
    4149{
    42     // Internal struct
    43     struct TclInterpreterBundle;
    44 
    4550    class _CoreExport TclThreadManager : public OrxonoxClass
    4651    {
    47         friend class IRC;
    4852        friend class TclBind;
     53        friend void tclThread(TclInterpreterBundle* bundle, std::string command);
    4954
    5055        public:
    5156            TclThreadManager(Tcl::interpreter* interpreter);
    52             ~TclThreadManager();
     57            virtual ~TclThreadManager();
    5358
    54             static TclThreadManager& getInstance() { assert(singletonRef_s); return *singletonRef_s; }
     59            static TclThreadManager& getInstance() { assert(TclThreadManager::singletonPtr_s); return *TclThreadManager::singletonPtr_s; }
    5560
    56             static unsigned int create();
    57             static unsigned int createID(unsigned int threadID);
    58             static void destroy(unsigned int threadID);
    59             static void execute(unsigned int threadID, const std::string& command);
    60             static std::string query(unsigned int threadID, const std::string& command);
    61             static void status();
    62             static void dump(unsigned int threadID);
    63             static void flush(unsigned int threadID);
     61            static unsigned int      create();
     62            static Tcl::interpreter* createWithId(unsigned int id);
     63            static void              destroy(unsigned int id);
     64            static void              execute(unsigned int target_id, const std::string& command);
     65            static std::string       query(unsigned int target_id, const std::string& command);
    6466
    6567            void error(const std::string& error);
     
    7173
    7274        private:
    73             TclThreadManager(const TclThreadManager& other);
     75            static void tcl_execute(const Tcl::object& args);
     76            static void tcl_crossexecute(int target_id, const Tcl::object& args);
     77            static std::string tcl_query(int source_id, const Tcl::object& args);
     78            static std::string tcl_crossquery(int source_id, int target_id, const Tcl::object& args);
     79            static bool tcl_running(int id);
    7480
    75             static void tcl_execute(Tcl::object const &args);
    76             static std::string tcl_query(int querierID, Tcl::object const &args);
    77             static std::string tcl_crossquery(int querierID, int threadID, Tcl::object const &args);
    78             static bool tcl_running(int threadID);
     81            void _execute(unsigned int target_id, const std::string& command);
     82            std::string _query(unsigned int source_id, unsigned int target_id, const std::string& command, bool bUseCommandExecutor = false);
    7983
    80             Tcl::interpreter* createNewTclInterpreter(const std::string& threadID);
    81             Tcl::interpreter* getTclInterpreter(unsigned int threadID);
    82             TclInterpreterBundle* getInterpreterBundle(unsigned int threadID);
     84            TclInterpreterBundle* getInterpreterBundle(unsigned int id);
    8385            std::string dumpList(const std::list<unsigned int>& list);
    8486
    85             void pushCommandToQueue(const std::string& command);
    86             void forceCommandToFrontOfQueue(const std::string& command);
    87             std::string popCommandFromQueue();
    88             bool queueIsEmpty();
     87            std::string eval(TclInterpreterBundle* bundle, const std::string& command);
    8988
    90             void pushCommandToQueue(unsigned int threadID, const std::string& command);
    91             std::string popCommandFromQueue(unsigned int threadID);
    92             bool queueIsEmpty(unsigned int threadID);
     89            static TclThreadManager* singletonPtr_s;                            ///< Singleton pointer
    9390
    94             bool updateQueriersList(TclInterpreterBundle* querier, TclInterpreterBundle* target);
    95 
    96             std::string evalQuery(unsigned int querierID, const std::string& command);
    97             std::string evalQuery(unsigned int querierID, unsigned int threadID, const std::string& command);
    98 
    99             unsigned int threadCounter_;
    100             TclInterpreterBundle* orxonoxInterpreterBundle_;
    101             std::map<unsigned int, TclInterpreterBundle*> interpreterBundles_;
    102 
    103             static TclThreadManager* singletonRef_s;
     91            unsigned int numInterpreterBundles_;                                ///< Number of created Tcl-interpreters (only used for auto-numbered interpreters, not affected by @ref createWithId)
     92            std::map<unsigned int, TclInterpreterBundle*> interpreterBundles_;  ///< A map containing all Tcl-interpreters
     93            boost::shared_mutex* interpreterBundlesMutex_;                      ///< A mutex used to synchronize threads when accessing @ref interpreterBundles_
     94            TclThreadList<std::string>* messageQueue_;                          ///< A queue to pass messages from Tcl-threads to the main thread
     95            boost::mutex* mainInterpreterMutex_;                                ///< A mutex to synchronize queries to the main interpreter
    10496    };
    10597
    106     _CoreExport void tclThread(TclInterpreterBundle* interpreterBundle, std::string command);
     98    _CoreExport void tclThread(TclInterpreterBundle* bundle, std::string command);
    10799}
    108100
Note: See TracChangeset for help on using the changeset viewer.