Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/core/TclThreadManager.cc @ 3593

Last change on this file since 3593 was 3370, checked in by rgrieder, 15 years ago

Merged resource branch back to the trunk. Changes:

  • Automated graphics loading by evaluating whether a GameState requires it
  • Using native Tcl library (x3n)

Windows users: Update your dependency package!

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