Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/resource/src/core/TclThreadManager.cc @ 3362

Last change on this file since 3362 was 3361, checked in by rgrieder, 15 years ago

Two little build fixes.

  • Property svn:eol-style set to native
File size: 29.0 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
[1792]29#include "TclThreadManager.h"
30
[1505]31#include <boost/bind.hpp>
[3304]32#include <boost/thread/thread.hpp>
[3318]33#include <boost/thread/locks.hpp>
34#include <boost/thread/shared_mutex.hpp>
[1505]35#include <OgreTimer.h>
[3196]36#include <cpptcl/cpptcl.h>
[1505]37
[3196]38#include "util/Convert.h"
[2896]39#include "Clock.h"
[3196]40#include "CommandExecutor.h"
41#include "ConsoleCommand.h"
[1505]42#include "CoreIncludes.h"
43#include "TclBind.h"
[3318]44#include "TclThreadList.h"
[1505]45
46namespace orxonox
47{
[1786]48    const float TCLTHREADMANAGER_MAX_CPU_USAGE = 0.50f;
[1784]49
[1747]50    SetConsoleCommandShortcutAlias(TclThreadManager, execute, "tclexecute").argumentCompleter(0, autocompletion::tclthreads());
51    SetConsoleCommandShortcutAlias(TclThreadManager, query,   "tclquery"  ).argumentCompleter(0, autocompletion::tclthreads());
[1505]52    SetConsoleCommand(TclThreadManager, create,  false);
[1747]53    SetConsoleCommand(TclThreadManager, destroy, false).argumentCompleter(0, autocompletion::tclthreads());
54    SetConsoleCommand(TclThreadManager, execute, false).argumentCompleter(0, autocompletion::tclthreads());
55    SetConsoleCommand(TclThreadManager, query,   false).argumentCompleter(0, autocompletion::tclthreads());
[3360]56    SetConsoleCommand(TclThreadManager, source,  false).argumentCompleter(0, autocompletion::tclthreads());
[1505]57
[3318]58    /**
59        @brief A struct containing all informations about a Tcl-interpreter
60    */
[3304]61    struct TclInterpreterBundle
62    {
[3318]63        TclInterpreterBundle()
64        {
65            this->lock_ = new boost::unique_lock<boost::mutex>(this->mutex_, boost::defer_lock);
66            this->bRunning_ = true;
67        }
[3304]68
[3318]69        ~TclInterpreterBundle()
70        {
71            delete this->lock_;
72        }
[3304]73
[3318]74        unsigned int                      id_;             ///< The id of the interpreter
75        Tcl::interpreter*                 interpreter_;    ///< The Tcl-interpreter
76        boost::mutex                      mutex_;          ///< A mutex to lock the interpreter while it's being used
77        boost::unique_lock<boost::mutex>* lock_;           ///< The corresponding lock for the mutex
78        TclThreadList<std::string>        queue_;          ///< The command queue for commands passed by execute(command)
79        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)
80        bool                              bRunning_;       ///< This variable stays true until destroy() gets called
[3304]81    };
82
[3318]83    TclThreadManager* TclThreadManager::singletonPtr_s = 0;
[3304]84
[3318]85    /**
86        @brief Constructor
87        @param interpreter A pointer to the standard Tcl-interpreter (see @ref TclBind)
88    */
[1792]89    TclThreadManager::TclThreadManager(Tcl::interpreter* interpreter)
[1505]90    {
91        RegisterRootObject(TclThreadManager);
92
[3318]93        assert(TclThreadManager::singletonPtr_s == 0);
94        TclThreadManager::singletonPtr_s = this;
[1792]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    {
[3318]118        TclThreadManager::singletonPtr_s = 0;
[1505]119
[3318]120        delete this->interpreterBundlesMutex_;
[3326]121//        delete this->mainInterpreterMutex_; // <-- temporary disabled to avoid crash if a thread is still actively queriyng
[3318]122        delete this->messageQueue_;
[1505]123    }
124
[3318]125    /**
126        @brief The "main loop" of the TclThreadManager. Creates new threads if needed and handles queries and queued commands for the main interpreter.
127    */
128    void TclThreadManager::update(const Clock& time)
[1505]129    {
[3318]130        // Get the bundle of the main interpreter (0)
131        TclInterpreterBundle* bundle = this->getInterpreterBundle(0);
[1505]132        if (bundle)
133        {
[3318]134            // Unlock the mutex to allow other threads accessing the main interpreter
135            bundle->lock_->unlock();
136
137            // Lock the main interpreter mutex once to synchronize with threads that want to query the main interpreter
[1505]138            {
[3318]139                boost::unique_lock<boost::mutex> lock(*this->mainInterpreterMutex_);
[1505]140            }
[3318]141
142            // Lock the mutex again to gain exclusive access to the interpreter for the rest of the mainloop
143            bundle->lock_->lock();
144
145            // Execute commands in the queues of the threaded interpreters
[1505]146            {
[3318]147                boost::shared_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_);
148                for (std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = this->interpreterBundles_.begin(); it != this->interpreterBundles_.end(); ++it)
[1505]149                {
[3318]150                    if (it->first == 0)
151                        continue; // We'll handle the default interpreter later (and without threads of course)
152
153                    TclInterpreterBundle* bundle = it->second;
154                    if (!bundle->queue_.empty())
[1505]155                    {
[3318]156                        // There are commands in the queue
[1505]157                        try
158                        {
[3318]159                            if (!bundle->lock_->owns_lock() && bundle->lock_->try_lock())
[1505]160                            {
[3318]161                                // We sucessfully obtained a lock for the interpreter
162                                std::string command;
163                                if (bundle->queue_.try_pop_front(&command))
164                                {
165                                    // Start a thread to execute the command
166                                    boost::thread(boost::bind(&tclThread, bundle, command));
167                                }
168                                else
169                                {
170                                    // Somehow the queue become empty (maybe multiple consumers) - unlock the mutex
171                                    bundle->lock_->unlock();
172                                }
[1505]173                            }
[3318]174                        }
175                        catch (...)
176                        {
177                            // A lock error occurred - this is possible if the lock gets locked between !bundle->lock_->owns_lock() and bundle->lock_->try_lock()
178                            // This isn't too bad, just continue
179                        }
[1505]180                    }
181                }
182            }
183
[3318]184            // Execute commands in the message queue
185            if (!this->messageQueue_->empty())
186            {
187                std::string command;
188                while (true)
189                {
190                    // Pop the front value from the list (break the loop if there are no elements in the list)
191                    if (!this->messageQueue_->try_pop_front(&command))
192                        break;
[3313]193
[3318]194                    // Execute the command
195                    CommandExecutor::execute(command, false);
196                }
[1505]197            }
198
[3318]199            // Execute commands in the queue of the main interpreter
200            if (!bundle->queue_.empty())
[1505]201            {
[3318]202                // Calculate the time we have until we reach the maximal cpu usage
203                unsigned long maxtime = (unsigned long)(time.getDeltaTime() * 1000000 * TCLTHREADMANAGER_MAX_CPU_USAGE);
[1505]204
[3318]205                Ogre::Timer timer;
206                std::string command;
[1505]207
[3318]208                while (timer.getMicroseconds() < maxtime)
209                {
210                    // Pop the front value from the list (break the loop if there are no elements in the list)
211                    if (!bundle->queue_.try_pop_front(&command))
212                        break;
[1505]213
[3318]214                    // Execute the command
215                    CommandExecutor::execute(command, false);
216                }
217            }
[1505]218        }
219    }
220
[3318]221    /**
222        @brief Creates a new Tcl-interpreter.
223    */
224    unsigned int TclThreadManager::create()
[1505]225    {
[3318]226        TclThreadManager::getInstance().numInterpreterBundles_++;
227        TclThreadManager::createWithId(TclThreadManager::getInstance().numInterpreterBundles_);
228        COUT(0) << "Created new Tcl-interpreter with ID " << TclThreadManager::getInstance().numInterpreterBundles_ << std::endl;
229        return TclThreadManager::getInstance().numInterpreterBundles_;
[1505]230    }
231
[3318]232    /**
233        @brief Creates a new Tcl-interpreter with a given id.
[1505]234
[3318]235        Use with caution - if the id collides with an already existing interpreter, this call will fail.
236        This will also be a problem, if the auto-numbered interpreters (by using create()) reach an id
237        which was previously used in this function. Use high numbers to be safe.
238    */
239    Tcl::interpreter* TclThreadManager::createWithId(unsigned int id)
[1505]240    {
[3318]241        TclInterpreterBundle* newbundle = new TclInterpreterBundle();
242        newbundle->id_ = id;
[3344]243        newbundle->interpreter_ = TclBind::createTclInterpreter();
[1505]244
[3360]245        TclThreadManager::initialize(newbundle);
246
247        {
248            // Add the new bundle to the map
249            boost::unique_lock<boost::shared_mutex> lock(*TclThreadManager::getInstance().interpreterBundlesMutex_);
250            TclThreadManager::getInstance().interpreterBundles_[id] = newbundle;
251        }
252
253        return newbundle->interpreter_;
254    }
255
256    void TclThreadManager::initialize(TclInterpreterBundle* bundle)
257    {
258        std::string id_string = getConvertedValue<unsigned int, std::string>(bundle->id_);
259
[3318]260        // Initialize the new interpreter
261        try
[3313]262        {
[3318]263            // Define the functions which are implemented in C++
[3360]264            bundle->interpreter_->def("::orxonox::execute",      TclThreadManager::tcl_execute,      Tcl::variadic());
265            bundle->interpreter_->def("::orxonox::crossexecute", TclThreadManager::tcl_crossexecute, Tcl::variadic());
266            bundle->interpreter_->def("::orxonox::query",        TclThreadManager::tcl_query,        Tcl::variadic());
267            bundle->interpreter_->def("::orxonox::crossquery",   TclThreadManager::tcl_crossquery,   Tcl::variadic());
268            bundle->interpreter_->def("::orxonox::running",      TclThreadManager::tcl_running);
[3313]269
[3318]270            // Create threadspecific shortcuts for the functions above
[3360]271            bundle->interpreter_->def("execute",      TclThreadManager::tcl_execute,      Tcl::variadic());
272            bundle->interpreter_->def("crossexecute", TclThreadManager::tcl_crossexecute, Tcl::variadic());
273            bundle->interpreter_->eval("proc query      {args}    { ::orxonox::query " + id_string + " $args }");
274            bundle->interpreter_->eval("proc crossquery {id args} { ::orxonox::crossquery " + id_string + " $id $args }");
275            bundle->interpreter_->eval("proc running    {}        { return [::orxonox::running " + id_string + "] }");
[1505]276
[3318]277            // Define a variable containing the thread id
[3360]278            bundle->interpreter_->eval("set id " + id_string);
[1505]279
[3318]280            // Use our own exit function to avoid shutting down the whole program instead of just the interpreter
[3360]281            bundle->interpreter_->eval("rename exit ::tcl::exit");
282            bundle->interpreter_->eval("proc exit {} { execute TclThreadManager destroy " + id_string + " }");
[1505]283
[3318]284            // Redefine some native functions
[3360]285            bundle->interpreter_->eval("rename while ::tcl::while");
286            bundle->interpreter_->eval("rename ::orxonox::while while");
287            bundle->interpreter_->eval("rename for ::tcl::for");
288            bundle->interpreter_->eval("rename ::orxonox::for for");
[3313]289        }
[3318]290        catch (const Tcl::tcl_error& e)
[3360]291        {   bundle->interpreter_ = 0; COUT(1) << "Tcl error while creating Tcl-interpreter (" << id_string << "): " << e.what() << std::endl;   }
[3318]292        catch (const std::exception& e)
[3360]293        {   bundle->interpreter_ = 0; COUT(1) << "Error while creating Tcl-interpreter (" << id_string << "): " << e.what() << std::endl;   }
[3318]294        catch (...)
[3360]295        {   bundle->interpreter_ = 0; COUT(1) << "An error occurred while creating a new Tcl-interpreter (" << id_string << ")" << std::endl;   }
[3313]296    }
297
[3318]298    /**
299        @brief Stops and destroys a given Tcl-interpreter
300    */
301    void TclThreadManager::destroy(unsigned int id)
[3313]302    {
[3318]303        // TODO
304        // Not yet implemented
[3350]305        TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(id);
306        if (bundle)
307        {
308            bundle->bRunning_ = false;
309        }
[3313]310    }
[1505]311
[3318]312    /**
313        @brief Sends a command to the queue of a given Tcl-interpreter
314        @param id The id of the target interpreter
315        @param command The command to be sent
316    */
317    void TclThreadManager::execute(unsigned int target_id, const std::string& command)
[3313]318    {
[3318]319        TclThreadManager::getInstance()._execute(target_id, command);
[3304]320    }
321
[3318]322    /**
323        @brief This function can be called from Tcl to execute a console command.
[3313]324
[3318]325        Commands which shall be executed are put into a queue and processed as soon as the
326        main thread feels ready to do so. The queue may also be full which results in a temporary
327        suspension of the calling thread until the queue gets ready again.
328    */
329    void TclThreadManager::tcl_execute(const Tcl::object& args)
[1505]330    {
[3318]331        TclThreadManager::getInstance()._execute(0, stripEnclosingBraces(args.get()));
[1505]332    }
333
[3318]334    /**
335        @brief This function can be called from Tcl to send a command to the queue of any interpreter.
336        @param target_id The id of the target thread
337    */
338    void TclThreadManager::tcl_crossexecute(int target_id, const Tcl::object& args)
[3313]339    {
[3318]340        TclThreadManager::getInstance()._execute(static_cast<unsigned int>(target_id), stripEnclosingBraces(args.get()));
[3313]341    }
[1505]342
[3318]343    /**
344        @brief Sends a command to the queue of a given Tcl-interpreter
345        @param id The id of the target interpreter
346        @param command The command to be sent
347    */
348    void TclThreadManager::_execute(unsigned int target_id, const std::string& command)
[1505]349    {
[3318]350        TclInterpreterBundle* bundle = this->getInterpreterBundle(target_id);
351        if (bundle)
352            bundle->queue_.push_back(command);
[1505]353    }
354
[3318]355
356    /**
357        @brief Sends a query to a given Tcl-interpreter and waits for the result
358        @param id The id of the target interpreter
359        @param command The command to be sent
360        @return The result of the command
361    */
362    std::string TclThreadManager::query(unsigned int target_id, const std::string& command)
[1505]363    {
[3318]364        return TclThreadManager::getInstance()._query(0, target_id, command);
[1505]365    }
366
[3318]367    /**
368        @brief This function can be called from Tcl to send a query to the main thread.
369        @param source_id The id of the calling thread
[3313]370
[3318]371        A query waits for the result of the command. This means, the calling thread will be blocked until
372        the main thread answers the query. In return, the main thread sends the result of the console
373        command back to Tcl.
374    */
375    std::string TclThreadManager::tcl_query(int source_id, const Tcl::object& args)
[3313]376    {
[3318]377        return TclThreadManager::getInstance()._query(static_cast<unsigned int>(source_id), 0, stripEnclosingBraces(args.get()), true);
[3313]378    }
[3307]379
[3318]380    /**
381        @brief This function can be called from Tcl to send a query to another thread.
382        @param source_id The id of the calling thread
383        @param target_id The id of the target thread
384    */
385    std::string TclThreadManager::tcl_crossquery(int source_id, int target_id, const Tcl::object& args)
[1505]386    {
[3318]387        return TclThreadManager::getInstance()._query(static_cast<unsigned int>(source_id), static_cast<unsigned int>(target_id), stripEnclosingBraces(args.get()));
[1505]388    }
389
[3318]390    /**
391        @brief This function performs a query to any Tcl interpreter
392        @param source_id The id of the calling thread
393        @param target_id The id of the target thread
394        @param command The command to send as a query
395        @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.
396    */
397    std::string TclThreadManager::_query(unsigned int source_id, unsigned int target_id, const std::string& command, bool bUseCommandExecutor)
[3313]398    {
[3318]399        TclInterpreterBundle* source_bundle = this->getInterpreterBundle(source_id);
400        TclInterpreterBundle* target_bundle = this->getInterpreterBundle(target_id);
401        std::string output;
[1505]402
[3318]403        if (source_bundle && target_bundle)
[3313]404        {
[3318]405            // 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)
406            // 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]407
[3318]408            if ((source_bundle->id_ == target_bundle->id_) || source_bundle->queriers_.is_in(target_bundle->id_))
[3313]409            {
[3318]410                // This query would lead to a deadlock - return with an error
[3360]411                TclThreadManager::error("Error: Circular query (" + this->dumpList(source_bundle->queriers_.getList()) + " " + getConvertedValue<unsigned int, std::string>(source_bundle->id_) \
[3318]412                            + " -> " + getConvertedValue<unsigned int, std::string>(target_bundle->id_) \
413                            + "), couldn't query Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(target_bundle->id_) \
414                            + " from other interpreter with ID " + getConvertedValue<unsigned int, std::string>(source_bundle->id_) + ".");
[3313]415            }
416            else
[3318]417            {
418                boost::unique_lock<boost::mutex> lock(target_bundle->mutex_, boost::try_to_lock);
419                boost::unique_lock<boost::mutex> mainlock(*this->mainInterpreterMutex_, boost::defer_lock);
[1505]420
[3318]421                if (!lock.owns_lock() && source_bundle->id_ != 0)
[1505]422                {
[3318]423                    // 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)
424                    if (target_bundle->id_ == 0)
[1505]425                    {
[3318]426                        // We're querying the main interpreter - use the main interpreter mutex to synchronize
427                        mainlock.lock();
428                        lock.lock();
429                    }
430                    else
431                    {
432                        // We're querying a threaded interpreter - no synchronization needed
433                        lock.lock();
434                    }
435                }
[1505]436
[3318]437                if (lock.owns_lock())
438                {
439                    // Now the mutex of target_bundle is also locked an we can update the querier list
440                    target_bundle->queriers_.insert(target_bundle->queriers_.getList().begin(), source_bundle->queriers_.getList().begin(), source_bundle->queriers_.getList().end());
441                    target_bundle->queriers_.push_back(source_bundle->id_);
[1505]442
[3318]443                    // Perform the query (note: this happens in the main thread because we need the returnvalue)
444                    if (target_bundle->id_ == 0 && bUseCommandExecutor)
[1505]445                    {
[3318]446                        // It's a query to the CommandExecutor
[3360]447                        TclThreadManager::debug("TclThread_query -> CE: " + command);
[3318]448                        if (!CommandExecutor::execute(command, false))
[3360]449                            TclThreadManager::error("Error: Can't execute command \"" + command + "\"!");
[3318]450
451                        if (CommandExecutor::getLastEvaluation().hasReturnvalue())
452                            output = CommandExecutor::getLastEvaluation().getReturnvalue().getString();
[1505]453                    }
454                    else
455                    {
[3318]456                        // It's a query to a Tcl interpreter
[3360]457                        TclThreadManager::debug("TclThread_query: " + command);
[3318]458
[3360]459                        output = TclThreadManager::eval(target_bundle, command, "query");
[1505]460                    }
[3318]461
462                    // Clear the queriers list of the target
463                    target_bundle->queriers_.clear();
464
465                    // Unlock the mutex of the target_bundle
466                    lock.unlock();
467
468                    // Finally unlock the main interpreter lock if necessary
469                    if (mainlock.owns_lock())
470                        mainlock.unlock();
[3313]471                }
[3318]472                else
473                {
474                    // This happens if the main thread tries to query a busy interpreter
475                    // To avoid a lock of the main thread, we simply don't proceed with the query in this case
[3360]476                    TclThreadManager::error("Error: Couldn't query Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(target_bundle->id_) + ", interpreter is busy right now.");
[3318]477                }
478            }
[3307]479
[1505]480        }
[3318]481
[1505]482        return output;
483    }
484
[3318]485    /**
[3360]486        @brief Creates a non-interactive Tcl-interpreter which executes a file.
487    */
488    void TclThreadManager::source(const std::string& file)
489    {
490        boost::thread(boost::bind(&sourceThread, file));
491    }
492
493    /**
[3318]494        @brief This function can be called from Tcl to ask if the thread is still suposed to be running.
495        @param id The id of the thread in question
496    */
497    bool TclThreadManager::tcl_running(int id)
[1505]498    {
[3318]499        TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(static_cast<unsigned int>(id));
500        if (bundle)
501            return bundle->bRunning_;
502        else
503            return false;
504    }
505
506    /**
507        @brief Returns the interpreter bundle with the given id.
508        @param id The id of the interpreter
509        @return The interpreter or 0 if the id doesn't exist
510    */
511    TclInterpreterBundle* TclThreadManager::getInterpreterBundle(unsigned int id)
512    {
513        boost::shared_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_);
514
515        std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = this->interpreterBundles_.find(id);
516        if (it != this->interpreterBundles_.end())
[1505]517        {
[3318]518            return it->second;
[1505]519        }
[3318]520        else
[1505]521        {
[3360]522            TclThreadManager::error("Error: No Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(id) + " existing.");
[3318]523            return 0;
[1505]524        }
[3318]525    }
[1505]526
[3318]527    /**
528        @brief Returns a string containing all elements of a unsigned-integer-list separated by spaces.
529    */
530    std::string TclThreadManager::dumpList(const std::list<unsigned int>& list)
531    {
532        std::string output = "";
533        for (std::list<unsigned int>::const_iterator it = list.begin(); it != list.end(); ++it)
[1505]534        {
[3318]535            if (it != list.begin())
536                output += " ";
537
538            output += getConvertedValue<unsigned int, std::string>(*it);
[1505]539        }
[3318]540        return output;
[1505]541    }
542
[3318]543    /**
544        @brief Returns a list with the numbers of all existing Tcl-interpreters.
545
546        This function is used by the auto completion function.
547    */
[1505]548    std::list<unsigned int> TclThreadManager::getThreadList() const
549    {
[3318]550        boost::shared_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_);
551
[1505]552        std::list<unsigned int> threads;
553        for (std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = this->interpreterBundles_.begin(); it != this->interpreterBundles_.end(); ++it)
[3318]554            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)
555                threads.push_back(it->first);
[1505]556        return threads;
557    }
558
[3318]559    /**
560        @brief A helper function to print errors in a thread safe manner.
561    */
562    void TclThreadManager::error(const std::string& error)
[1505]563    {
[3360]564        TclThreadManager::getInstance().messageQueue_->push_back("error " + error);
[3318]565    }
566
567    /**
568        @brief A helper function to print debug information in a thread safe manner.
569    */
570    void TclThreadManager::debug(const std::string& error)
571    {
[3360]572        TclThreadManager::getInstance().messageQueue_->push_back("debug " + error);
[3318]573    }
574
575    /**
576        @brief Evaluates a Tcl command without throwing exceptions (which may rise problems on certain machines).
577        @return The Tcl return value
578
579        Errors are reported through the @ref error function.
580    */
[3360]581    std::string TclThreadManager::eval(TclInterpreterBundle* bundle, const std::string& command, const std::string& action)
[3318]582    {
583        Tcl_Interp* interpreter = bundle->interpreter_->get();
584        int cc = Tcl_Eval(interpreter, command.c_str());
585
586        Tcl::details::result result(interpreter);
587
588        if (cc != TCL_OK)
[1505]589        {
[3360]590            TclThreadManager::error("Tcl error (" + action + ", ID " + getConvertedValue<unsigned int, std::string>(bundle->id_) + "): " + static_cast<std::string>(result));
[3318]591            return "";
[1505]592        }
[3318]593        else
[1505]594        {
[3318]595            return result;
[1505]596        }
[3318]597    }
[1505]598
[3318]599    ////////////////
600    // The Thread //
601    ////////////////
602
603    /**
604        @brief The main function of the thread. Executes a Tcl command.
605        @param bundle The interpreter bundle containing all necessary variables
606        @param command the Command to execute
607    */
608    void tclThread(TclInterpreterBundle* bundle, std::string command)
609    {
[3360]610        TclThreadManager::debug("TclThread_execute: " + command);
[3318]611
[3360]612        TclThreadManager::eval(bundle, command, "execute");
[3318]613
614        bundle->lock_->unlock();
[1505]615    }
[3360]616
617    /**
618        @brief The main function of a non-interactive source thread. Executes the file.
619        @param file The name of the file that should be executed by the non-interactive interpreter.
620    */
621    void sourceThread(std::string file)
622    {
623        TclThreadManager::debug("TclThread_source: " + file);
624
625        // Prepare the command-line arguments
[3361]626        const int argc = 2;
[3360]627        char* argv[argc];
628        argv[0] = "tclthread";
629        argv[1] = const_cast<char*>(file.c_str());
630
631        // Start the Tcl-command Tcl_Main with the Tcl_OrxonoxAppInit hook
632        Tcl_Main(argc, argv, Tcl_OrxonoxAppInit);
633
634//        Tcl::object object(file);
635//        int cc = Tcl_FSEvalFile(bundle->interpreter_->get(), object.get_object());
636//        Tcl::details::result result(bundle->interpreter_->get());
637//        if (cc != TCL_OK)
638//            TclThreadManager::error("Tcl error (source, ID " + getConvertedValue<unsigned int, std::string>(bundle->id_) + "): " + static_cast<std::string>(result));
639//
640//        // Unlock the mutex
641//        bundle->lock_->unlock();
642    }
643
644    /**
645        @brief A tcl-init hook to inject the non-interactive Tcl-interpreter into the TclThreadManager.
646    */
647    int Tcl_OrxonoxAppInit(Tcl_Interp* interp)
648    {
649        // Create a new interpreter bundle
650        unsigned int id = TclThreadManager::create();
651        TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(id);
652
653        // Replace the default interpreter in the bundle with the non-interactive one (passed as an argument to this function)
654        if (bundle->interpreter_)
655            delete bundle->interpreter_;
656        bundle->interpreter_ = new Tcl::interpreter(interp, true);
657
658        // Initialize the non-interactive interpreter (like in @ref TclBind::createTclInterpreter but exception safe)
659        std::string libpath = TclBind::getTclLibraryPath();
660        if (libpath != "")
661            TclThreadManager::eval(bundle, "set tcl_library \"" + libpath + "\"", "source");
662        int cc = Tcl_Init(interp);
663        TclThreadManager::eval(bundle, "source \"" + TclBind::getInstance().getTclDataPath() + "/init.tcl\"", "source");
664
665        // Initialize the non-interactive interpreter also with the thread-specific stuff
666        TclThreadManager::initialize(bundle);
667
668        // Lock the mutex (this will be locked until the thread finishes - no chance to interact with the interpreter)
669        bundle->lock_->lock();
670
671        // Return to Tcl_Main
672        if (!bundle->interpreter_)
673            return TCL_ERROR;
674        else
675            return cc;
676    }
[1505]677}
Note: See TracBrowser for help on using the repository browser.