Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/command/TclThreadManager.cc @ 8351

Last change on this file since 8351 was 8351, checked in by rgrieder, 13 years ago

Merged kicklib2 branch back to trunk (includes former branches ois_update, mac_osx and kicklib).

Notes for updating

Linux:
You don't need an extra package for CEGUILua and Tolua, it's already shipped with CEGUI.
However you do need to make sure that the OgreRenderer is installed too with CEGUI 0.7 (may be a separate package).
Also, Orxonox now recognises if you install the CgProgramManager (a separate package available on newer Ubuntu on Debian systems).

Windows:
Download the new dependency packages versioned 6.0 and use these. If you have problems with that or if you don't like the in game console problem mentioned below, you can download the new 4.3 version of the packages (only available for Visual Studio 2005/2008).

Key new features:

  • *Support for Mac OS X*
  • Visual Studio 2010 support
  • Bullet library update to 2.77
  • OIS library update to 1.3
  • Support for CEGUI 0.7 —> Support for Arch Linux and even SuSE
  • Improved install target
  • Compiles now with GCC 4.6
  • Ogre Cg Shader plugin activated for Linux if available
  • And of course lots of bug fixes

There are also some regressions:

  • No support for CEGUI 0.5, Ogre 1.4 and boost 1.35 - 1.39 any more
  • In game console is not working in main menu for CEGUI 0.7
  • Tolua (just the C lib, not the application) and CEGUILua libraries are no longer in our repository. —> You will need to get these as well when compiling Orxonox
  • And of course lots of new bugs we don't yet know about
  • Property svn:eol-style set to native
File size: 29.1 KB
RevLine 
[1505]1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
[7401]29/**
30    @file
31    @brief Implementation of TclThreadManager.
32*/
33
[1792]34#include "TclThreadManager.h"
35
[1505]36#include <boost/bind.hpp>
[3304]37#include <boost/thread/thread.hpp>
[3318]38#include <boost/thread/locks.hpp>
39#include <boost/thread/shared_mutex.hpp>
[1505]40#include <OgreTimer.h>
[3196]41#include <cpptcl/cpptcl.h>
[1505]42
[5929]43#include "util/Clock.h"
[3196]44#include "util/Convert.h"
[5747]45#include "util/Exception.h"
[6417]46#include "util/StringUtils.h"
[7203]47#include "core/CoreIncludes.h"
[3196]48#include "CommandExecutor.h"
49#include "ConsoleCommand.h"
[1505]50#include "TclBind.h"
[3318]51#include "TclThreadList.h"
[1505]52
53namespace orxonox
54{
[1786]55    const float TCLTHREADMANAGER_MAX_CPU_USAGE = 0.50f;
[1784]56
[7236]57    SetConsoleCommand("TclThreadManager", "create",  &TclThreadManager::create);
58    SetConsoleCommand("TclThreadManager", "destroy", &TclThreadManager::destroy).argumentCompleter(0, autocompletion::tclthreads());
59    SetConsoleCommand("TclThreadManager", "execute", &TclThreadManager::execute).argumentCompleter(0, autocompletion::tclthreads());
60    SetConsoleCommand("TclThreadManager", "query",   &TclThreadManager::query  ).argumentCompleter(0, autocompletion::tclthreads());
61    SetConsoleCommand("TclThreadManager", "source",  &TclThreadManager::source ).argumentCompleter(0, autocompletion::tclthreads());
[1505]62
[3318]63    /**
[5695]64        @brief A struct containing all information about a Tcl-interpreter
[3318]65    */
[3304]66    struct TclInterpreterBundle
67    {
[3318]68        TclInterpreterBundle()
69        {
70            this->lock_ = new boost::unique_lock<boost::mutex>(this->mutex_, boost::defer_lock);
71            this->bRunning_ = true;
72        }
[3304]73
[3318]74        ~TclInterpreterBundle()
75        {
76            delete this->lock_;
77        }
[3304]78
[3318]79        unsigned int                      id_;             ///< The id of the interpreter
80        Tcl::interpreter*                 interpreter_;    ///< The Tcl-interpreter
81        boost::mutex                      mutex_;          ///< A mutex to lock the interpreter while it's being used
82        boost::unique_lock<boost::mutex>* lock_;           ///< The corresponding lock for the mutex
83        TclThreadList<std::string>        queue_;          ///< The command queue for commands passed by execute(command)
84        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)
85        bool                              bRunning_;       ///< This variable stays true until destroy() gets called
[3304]86    };
87
[3318]88    TclThreadManager* TclThreadManager::singletonPtr_s = 0;
[3304]89
[3318]90    /**
91        @brief Constructor
92        @param interpreter A pointer to the standard Tcl-interpreter (see @ref TclBind)
93    */
[1792]94    TclThreadManager::TclThreadManager(Tcl::interpreter* interpreter)
[1505]95    {
[3318]96        this->numInterpreterBundles_ = 0;
[1505]97
[3318]98        this->interpreterBundlesMutex_ = new boost::shared_mutex();
99        this->mainInterpreterMutex_ = new boost::mutex();
100        this->messageQueue_ = new TclThreadList<std::string>();
101
102        TclInterpreterBundle* newbundle = new TclInterpreterBundle();
103        newbundle->id_ = 0;
104        newbundle->interpreter_ = interpreter;
105        newbundle->lock_->lock();
106
[3313]107        {
[3318]108            boost::unique_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_);
109            this->interpreterBundles_[0] = newbundle;
[3313]110        }
[1505]111    }
112
[3318]113    /**
114        @brief Destructor
115    */
116    TclThreadManager::~TclThreadManager()
[1505]117    {
[8351]118        delete this->interpreterBundles_[0];
[3318]119        delete this->interpreterBundlesMutex_;
[8351]120//        delete this->mainInterpreterMutex_; // <-- temporarily disabled to avoid crash if a thread is still actively querying
[3318]121        delete this->messageQueue_;
[1505]122    }
123
[3318]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    */
[6417]127    void TclThreadManager::preUpdate(const Clock& time)
[1505]128    {
[3318]129        // Get the bundle of the main interpreter (0)
130        TclInterpreterBundle* bundle = this->getInterpreterBundle(0);
[1505]131        if (bundle)
132        {
[3318]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
[1505]137            {
[3318]138                boost::unique_lock<boost::mutex> lock(*this->mainInterpreterMutex_);
[1505]139            }
[3318]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
[1505]145            {
[3318]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)
[1505]148                {
[3318]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())
[1505]154                    {
[3318]155                        // There are commands in the queue
[1505]156                        try
157                        {
[3318]158                            if (!bundle->lock_->owns_lock() && bundle->lock_->try_lock())
[1505]159                            {
[3318]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                                }
[1505]172                            }
[3318]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                        }
[1505]179                    }
180                }
181            }
182
[3318]183            // Execute commands in the message queue
184            if (!this->messageQueue_->empty())
185            {
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;
[3313]192
[3318]193                    // Execute the command
194                    CommandExecutor::execute(command, false);
195                }
[1505]196            }
197
[3318]198            // Execute commands in the queue of the main interpreter
199            if (!bundle->queue_.empty())
[1505]200            {
[3318]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);
[1505]203
[3318]204                Ogre::Timer timer;
205                std::string command;
[1505]206
[3318]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;
[1505]212
[3318]213                    // Execute the command
214                    CommandExecutor::execute(command, false);
215                }
216            }
[1505]217        }
218    }
219
[3318]220    /**
221        @brief Creates a new Tcl-interpreter.
222    */
223    unsigned int TclThreadManager::create()
[1505]224    {
[3318]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_;
[1505]229    }
230
[3318]231    /**
232        @brief Creates a new Tcl-interpreter with a given id.
[1505]233
[3318]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)
[1505]239    {
[3318]240        TclInterpreterBundle* newbundle = new TclInterpreterBundle();
241        newbundle->id_ = id;
[3370]242        newbundle->interpreter_ = TclBind::createTclInterpreter();
[1505]243
[3370]244        TclThreadManager::initialize(newbundle);
245
246        {
247            // Add the new bundle to the map
248            boost::unique_lock<boost::shared_mutex> lock(*TclThreadManager::getInstance().interpreterBundlesMutex_);
249            TclThreadManager::getInstance().interpreterBundles_[id] = newbundle;
250        }
251
252        return newbundle->interpreter_;
253    }
254
255    void TclThreadManager::initialize(TclInterpreterBundle* bundle)
256    {
[7401]257        const std::string& id_string = multi_cast<std::string>(bundle->id_);
[3370]258
[3318]259        // Initialize the new interpreter
260        try
[3313]261        {
[3318]262            // Define the functions which are implemented in C++
[3370]263            bundle->interpreter_->def("::orxonox::execute",      TclThreadManager::tcl_execute,      Tcl::variadic());
264            bundle->interpreter_->def("::orxonox::crossexecute", TclThreadManager::tcl_crossexecute, Tcl::variadic());
265            bundle->interpreter_->def("::orxonox::query",        TclThreadManager::tcl_query,        Tcl::variadic());
266            bundle->interpreter_->def("::orxonox::crossquery",   TclThreadManager::tcl_crossquery,   Tcl::variadic());
267            bundle->interpreter_->def("::orxonox::running",      TclThreadManager::tcl_running);
[3313]268
[3318]269            // Create threadspecific shortcuts for the functions above
[3370]270            bundle->interpreter_->def("execute",      TclThreadManager::tcl_execute,      Tcl::variadic());
271            bundle->interpreter_->def("crossexecute", TclThreadManager::tcl_crossexecute, Tcl::variadic());
272            bundle->interpreter_->eval("proc query      {args}    { ::orxonox::query " + id_string + " $args }");
273            bundle->interpreter_->eval("proc crossquery {id args} { ::orxonox::crossquery " + id_string + " $id $args }");
274            bundle->interpreter_->eval("proc running    {}        { return [::orxonox::running " + id_string + "] }");
[1505]275
[3318]276            // Define a variable containing the thread id
[3370]277            bundle->interpreter_->eval("set id " + id_string);
[1505]278
[3318]279            // Use our own exit function to avoid shutting down the whole program instead of just the interpreter
[3370]280            bundle->interpreter_->eval("rename exit ::tcl::exit");
281            bundle->interpreter_->eval("proc exit {} { execute TclThreadManager destroy " + id_string + " }");
[1505]282
[3318]283            // Redefine some native functions
[3370]284            bundle->interpreter_->eval("rename while ::tcl::while");
285            bundle->interpreter_->eval("rename ::orxonox::while while");
286            bundle->interpreter_->eval("rename for ::tcl::for");
287            bundle->interpreter_->eval("rename ::orxonox::for for");
[3313]288        }
[3318]289        catch (const Tcl::tcl_error& e)
[3370]290        {   bundle->interpreter_ = 0; COUT(1) << "Tcl error while creating Tcl-interpreter (" << id_string << "): " << e.what() << std::endl;   }
[3313]291    }
292
[3318]293    /**
294        @brief Stops and destroys a given Tcl-interpreter
295    */
296    void TclThreadManager::destroy(unsigned int id)
[3313]297    {
[3318]298        // TODO
299        // Not yet implemented
[3370]300        TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(id);
301        if (bundle)
302        {
303            bundle->bRunning_ = false;
304        }
[3313]305    }
[1505]306
[3318]307    /**
308        @brief Sends a command to the queue of a given Tcl-interpreter
[7401]309        @param target_id The id of the target interpreter
[3318]310        @param command The command to be sent
311    */
312    void TclThreadManager::execute(unsigned int target_id, const std::string& command)
[3313]313    {
[3318]314        TclThreadManager::getInstance()._execute(target_id, command);
[3304]315    }
316
[3318]317    /**
318        @brief This function can be called from Tcl to execute a console command.
[3313]319
[3318]320        Commands which shall be executed are put into a queue and processed as soon as the
321        main thread feels ready to do so. The queue may also be full which results in a temporary
322        suspension of the calling thread until the queue gets ready again.
323    */
324    void TclThreadManager::tcl_execute(const Tcl::object& args)
[1505]325    {
[3318]326        TclThreadManager::getInstance()._execute(0, stripEnclosingBraces(args.get()));
[1505]327    }
328
[3318]329    /**
330        @brief This function can be called from Tcl to send a command to the queue of any interpreter.
331        @param target_id The id of the target thread
[7401]332        @param args Contains the content of the command
[3318]333    */
334    void TclThreadManager::tcl_crossexecute(int target_id, const Tcl::object& args)
[3313]335    {
[3318]336        TclThreadManager::getInstance()._execute(static_cast<unsigned int>(target_id), stripEnclosingBraces(args.get()));
[3313]337    }
[1505]338
[3318]339    /**
340        @brief Sends a command to the queue of a given Tcl-interpreter
[7401]341        @param target_id The id of the target interpreter
[3318]342        @param command The command to be sent
343    */
344    void TclThreadManager::_execute(unsigned int target_id, const std::string& command)
[1505]345    {
[3318]346        TclInterpreterBundle* bundle = this->getInterpreterBundle(target_id);
347        if (bundle)
348            bundle->queue_.push_back(command);
[1505]349    }
350
[3318]351
352    /**
353        @brief Sends a query to a given Tcl-interpreter and waits for the result
[7401]354        @param target_id The id of the target interpreter
[3318]355        @param command The command to be sent
356        @return The result of the command
357    */
358    std::string TclThreadManager::query(unsigned int target_id, const std::string& command)
[1505]359    {
[3318]360        return TclThreadManager::getInstance()._query(0, target_id, command);
[1505]361    }
362
[3318]363    /**
364        @brief This function can be called from Tcl to send a query to the main thread.
365        @param source_id The id of the calling thread
[7401]366        @param args Contains the content of the query
[3313]367
[3318]368        A query waits for the result of the command. This means, the calling thread will be blocked until
369        the main thread answers the query. In return, the main thread sends the result of the console
370        command back to Tcl.
371    */
372    std::string TclThreadManager::tcl_query(int source_id, const Tcl::object& args)
[3313]373    {
[3318]374        return TclThreadManager::getInstance()._query(static_cast<unsigned int>(source_id), 0, stripEnclosingBraces(args.get()), true);
[3313]375    }
[3307]376
[3318]377    /**
378        @brief This function can be called from Tcl to send a query to another thread.
379        @param source_id The id of the calling thread
380        @param target_id The id of the target thread
[7401]381        @param args Contains the content of the query
[3318]382    */
383    std::string TclThreadManager::tcl_crossquery(int source_id, int target_id, const Tcl::object& args)
[1505]384    {
[3318]385        return TclThreadManager::getInstance()._query(static_cast<unsigned int>(source_id), static_cast<unsigned int>(target_id), stripEnclosingBraces(args.get()));
[1505]386    }
387
[3318]388    /**
389        @brief This function performs a query to any Tcl interpreter
390        @param source_id The id of the calling thread
391        @param target_id The id of the target thread
392        @param command The command to send as a query
[7401]393        @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().
[3318]394    */
395    std::string TclThreadManager::_query(unsigned int source_id, unsigned int target_id, const std::string& command, bool bUseCommandExecutor)
[3313]396    {
[3318]397        TclInterpreterBundle* source_bundle = this->getInterpreterBundle(source_id);
398        TclInterpreterBundle* target_bundle = this->getInterpreterBundle(target_id);
399        std::string output;
[1505]400
[3318]401        if (source_bundle && target_bundle)
[3313]402        {
[3318]403            // 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)
404            // 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)
[3313]405
[3318]406            if ((source_bundle->id_ == target_bundle->id_) || source_bundle->queriers_.is_in(target_bundle->id_))
[3313]407            {
[3318]408                // This query would lead to a deadlock - return with an error
[7401]409                TclThreadManager::error("Error: Circular query (" + this->dumpList(source_bundle->queriers_.getList()) + ' ' + multi_cast<std::string>(source_bundle->id_) \
410                            + " -> " + multi_cast<std::string>(target_bundle->id_) \
411                            + "), couldn't query Tcl-interpreter with ID " + multi_cast<std::string>(target_bundle->id_) \
412                            + " from other interpreter with ID " + multi_cast<std::string>(source_bundle->id_) + '.');
[3313]413            }
414            else
[3318]415            {
416                boost::unique_lock<boost::mutex> lock(target_bundle->mutex_, boost::try_to_lock);
417                boost::unique_lock<boost::mutex> mainlock(*this->mainInterpreterMutex_, boost::defer_lock);
[1505]418
[3318]419                if (!lock.owns_lock() && source_bundle->id_ != 0)
[1505]420                {
[3318]421                    // 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)
422                    if (target_bundle->id_ == 0)
[1505]423                    {
[3318]424                        // We're querying the main interpreter - use the main interpreter mutex to synchronize
425                        mainlock.lock();
426                        lock.lock();
427                    }
428                    else
429                    {
430                        // We're querying a threaded interpreter - no synchronization needed
431                        lock.lock();
432                    }
433                }
[1505]434
[3318]435                if (lock.owns_lock())
436                {
437                    // Now the mutex of target_bundle is also locked an we can update the querier list
438                    target_bundle->queriers_.insert(target_bundle->queriers_.getList().begin(), source_bundle->queriers_.getList().begin(), source_bundle->queriers_.getList().end());
439                    target_bundle->queriers_.push_back(source_bundle->id_);
[1505]440
[3318]441                    // Perform the query (note: this happens in the main thread because we need the returnvalue)
442                    if (target_bundle->id_ == 0 && bUseCommandExecutor)
[1505]443                    {
[3318]444                        // It's a query to the CommandExecutor
[3370]445                        TclThreadManager::debug("TclThread_query -> CE: " + command);
[7228]446                        int error;
447                        output = CommandExecutor::query(command, &error, false);
448                        switch (error)
449                        {
450                            case CommandExecutor::Error:       TclThreadManager::error("Error: Can't execute command \"" + command + "\", command doesn't exist. (T)"); break;
451                            case CommandExecutor::Incomplete:  TclThreadManager::error("Error: Can't execute command \"" + command + "\", not enough arguments given. (T)"); break;
452                            case CommandExecutor::Deactivated: TclThreadManager::error("Error: Can't execute command \"" + command + "\", command is not active. (T)"); break;
453                            case CommandExecutor::Denied:      TclThreadManager::error("Error: Can't execute command \"" + command + "\", access denied. (T)"); break;
454                        }
[1505]455                    }
456                    else
457                    {
[3318]458                        // It's a query to a Tcl interpreter
[3370]459                        TclThreadManager::debug("TclThread_query: " + command);
[3318]460
[3370]461                        output = TclThreadManager::eval(target_bundle, command, "query");
[1505]462                    }
[3318]463
464                    // Clear the queriers list of the target
465                    target_bundle->queriers_.clear();
466
467                    // Unlock the mutex of the target_bundle
468                    lock.unlock();
469
470                    // Finally unlock the main interpreter lock if necessary
471                    if (mainlock.owns_lock())
472                        mainlock.unlock();
[3313]473                }
[3318]474                else
475                {
476                    // This happens if the main thread tries to query a busy interpreter
477                    // To avoid a lock of the main thread, we simply don't proceed with the query in this case
[7401]478                    TclThreadManager::error("Error: Couldn't query Tcl-interpreter with ID " + multi_cast<std::string>(target_bundle->id_) + ", interpreter is busy right now.");
[3318]479                }
480            }
[3307]481
[1505]482        }
[3318]483
[1505]484        return output;
485    }
486
[3318]487    /**
[3370]488        @brief Creates a non-interactive Tcl-interpreter which executes a file.
489    */
490    void TclThreadManager::source(const std::string& file)
491    {
492        boost::thread(boost::bind(&sourceThread, file));
493    }
494
495    /**
[3318]496        @brief This function can be called from Tcl to ask if the thread is still suposed to be running.
497        @param id The id of the thread in question
498    */
499    bool TclThreadManager::tcl_running(int id)
[1505]500    {
[3318]501        TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(static_cast<unsigned int>(id));
502        if (bundle)
503            return bundle->bRunning_;
504        else
505            return false;
506    }
507
508    /**
509        @brief Returns the interpreter bundle with the given id.
510        @param id The id of the interpreter
511        @return The interpreter or 0 if the id doesn't exist
512    */
513    TclInterpreterBundle* TclThreadManager::getInterpreterBundle(unsigned int id)
514    {
515        boost::shared_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_);
516
517        std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = this->interpreterBundles_.find(id);
518        if (it != this->interpreterBundles_.end())
[1505]519        {
[3318]520            return it->second;
[1505]521        }
[3318]522        else
[1505]523        {
[7401]524            TclThreadManager::error("Error: No Tcl-interpreter with ID " + multi_cast<std::string>(id) + " existing.");
[3318]525            return 0;
[1505]526        }
[3318]527    }
[1505]528
[3318]529    /**
530        @brief Returns a string containing all elements of a unsigned-integer-list separated by spaces.
531    */
532    std::string TclThreadManager::dumpList(const std::list<unsigned int>& list)
533    {
[6417]534        std::string output;
[3318]535        for (std::list<unsigned int>::const_iterator it = list.begin(); it != list.end(); ++it)
[1505]536        {
[3318]537            if (it != list.begin())
[6417]538                output += ' ';
[3318]539
[7401]540            output += multi_cast<std::string>(*it);
[1505]541        }
[3318]542        return output;
[1505]543    }
544
[3318]545    /**
546        @brief Returns a list with the numbers of all existing Tcl-interpreters.
547
548        This function is used by the auto completion function.
549    */
[1505]550    std::list<unsigned int> TclThreadManager::getThreadList() const
551    {
[3318]552        boost::shared_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_);
553
[1505]554        std::list<unsigned int> threads;
555        for (std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = this->interpreterBundles_.begin(); it != this->interpreterBundles_.end(); ++it)
[3318]556            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)
557                threads.push_back(it->first);
[1505]558        return threads;
559    }
560
[3318]561    /**
562        @brief A helper function to print errors in a thread safe manner.
563    */
564    void TclThreadManager::error(const std::string& error)
[1505]565    {
[3370]566        TclThreadManager::getInstance().messageQueue_->push_back("error " + error);
[3318]567    }
568
569    /**
570        @brief A helper function to print debug information in a thread safe manner.
571    */
572    void TclThreadManager::debug(const std::string& error)
573    {
[3370]574        TclThreadManager::getInstance().messageQueue_->push_back("debug " + error);
[3318]575    }
576
577    /**
578        @brief Evaluates a Tcl command without throwing exceptions (which may rise problems on certain machines).
579        @return The Tcl return value
580
581        Errors are reported through the @ref error function.
582    */
[3370]583    std::string TclThreadManager::eval(TclInterpreterBundle* bundle, const std::string& command, const std::string& action)
[3318]584    {
585        Tcl_Interp* interpreter = bundle->interpreter_->get();
586        int cc = Tcl_Eval(interpreter, command.c_str());
587
588        Tcl::details::result result(interpreter);
589
590        if (cc != TCL_OK)
[1505]591        {
[7401]592            TclThreadManager::error("Tcl error (" + action + ", ID " + multi_cast<std::string>(bundle->id_) + "): " + static_cast<std::string>(result));
[3318]593            return "";
[1505]594        }
[3318]595        else
[1505]596        {
[3318]597            return result;
[1505]598        }
[3318]599    }
[1505]600
[3318]601    ////////////////
602    // The Thread //
603    ////////////////
604
605    /**
606        @brief The main function of the thread. Executes a Tcl command.
607        @param bundle The interpreter bundle containing all necessary variables
608        @param command the Command to execute
609    */
[6417]610    void tclThread(TclInterpreterBundle* bundle, const std::string& command)
[3318]611    {
[3370]612        TclThreadManager::debug("TclThread_execute: " + command);
[3318]613
[3370]614        TclThreadManager::eval(bundle, command, "execute");
[3318]615
616        bundle->lock_->unlock();
[1505]617    }
[3370]618
619    /**
620        @brief The main function of a non-interactive source thread. Executes the file.
621        @param file The name of the file that should be executed by the non-interactive interpreter.
622    */
[6417]623    void sourceThread(const std::string& file)
[3370]624    {
625        TclThreadManager::debug("TclThread_source: " + file);
626
627        // Prepare the command-line arguments
628        const int argc = 2;
629        char* argv[argc];
[7165]630        argv[0] = const_cast<char*>("tclthread");
[3370]631        argv[1] = const_cast<char*>(file.c_str());
632
633        // Start the Tcl-command Tcl_Main with the Tcl_OrxonoxAppInit hook
634        Tcl_Main(argc, argv, Tcl_OrxonoxAppInit);
635
636//        Tcl::object object(file);
637//        int cc = Tcl_FSEvalFile(bundle->interpreter_->get(), object.get_object());
638//        Tcl::details::result result(bundle->interpreter_->get());
639//        if (cc != TCL_OK)
640//            TclThreadManager::error("Tcl error (source, ID " + getConvertedValue<unsigned int, std::string>(bundle->id_) + "): " + static_cast<std::string>(result));
641//
642//        // Unlock the mutex
643//        bundle->lock_->unlock();
644    }
645
646    /**
647        @brief A tcl-init hook to inject the non-interactive Tcl-interpreter into the TclThreadManager.
648    */
649    int Tcl_OrxonoxAppInit(Tcl_Interp* interp)
650    {
651        // Create a new interpreter bundle
652        unsigned int id = TclThreadManager::create();
653        TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(id);
654
655        // Replace the default interpreter in the bundle with the non-interactive one (passed as an argument to this function)
656        if (bundle->interpreter_)
657            delete bundle->interpreter_;
658        bundle->interpreter_ = new Tcl::interpreter(interp, true);
659
660        // Initialize the non-interactive interpreter (like in @ref TclBind::createTclInterpreter but exception safe)
[6417]661        const std::string& libpath = TclBind::getTclLibraryPath();
662        if (!libpath.empty())
663            TclThreadManager::eval(bundle, "set tcl_library \"" + libpath + '"', "source");
[3370]664        int cc = Tcl_Init(interp);
665        TclThreadManager::eval(bundle, "source \"" + TclBind::getInstance().getTclDataPath() + "/init.tcl\"", "source");
666
667        // Initialize the non-interactive interpreter also with the thread-specific stuff
668        TclThreadManager::initialize(bundle);
669
670        // Lock the mutex (this will be locked until the thread finishes - no chance to interact with the interpreter)
671        bundle->lock_->lock();
672
673        // Return to Tcl_Main
674        if (!bundle->interpreter_)
675            return TCL_ERROR;
676        else
677            return cc;
678    }
[1505]679}
Note: See TracBrowser for help on using the repository browser.