Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 3344 was 3344, checked in by landauf, 16 years ago

tcl uses it's native library, orxonox adds new features later (defined in media/tcl/init.tcl)

  • Property svn:eol-style set to native
File size: 25.8 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());
[1505]56
[3318]57    /**
58        @brief A struct containing all informations about a Tcl-interpreter
59    */
[3304]60    struct TclInterpreterBundle
61    {
[3318]62        TclInterpreterBundle()
63        {
64            this->lock_ = new boost::unique_lock<boost::mutex>(this->mutex_, boost::defer_lock);
65            this->bRunning_ = true;
66        }
[3304]67
[3318]68        ~TclInterpreterBundle()
69        {
70            delete this->lock_;
71        }
[3304]72
[3318]73        unsigned int                      id_;             ///< The id of the interpreter
74        Tcl::interpreter*                 interpreter_;    ///< The Tcl-interpreter
75        boost::mutex                      mutex_;          ///< A mutex to lock the interpreter while it's being used
76        boost::unique_lock<boost::mutex>* lock_;           ///< The corresponding lock for the mutex
77        TclThreadList<std::string>        queue_;          ///< The command queue for commands passed by execute(command)
78        TclThreadList<unsigned int>       queriers_;       ///< A list containing the id's of all other threads sending a query to this interpreter (to avoid circular queries and deadlocks)
79        bool                              bRunning_;       ///< This variable stays true until destroy() gets called
[3304]80    };
81
[3318]82    TclThreadManager* TclThreadManager::singletonPtr_s = 0;
[3304]83
[3318]84    /**
85        @brief Constructor
86        @param interpreter A pointer to the standard Tcl-interpreter (see @ref TclBind)
87    */
[1792]88    TclThreadManager::TclThreadManager(Tcl::interpreter* interpreter)
[1505]89    {
90        RegisterRootObject(TclThreadManager);
91
[3318]92        assert(TclThreadManager::singletonPtr_s == 0);
93        TclThreadManager::singletonPtr_s = this;
[1792]94
[3318]95        this->numInterpreterBundles_ = 0;
[1505]96
[3318]97        this->interpreterBundlesMutex_ = new boost::shared_mutex();
98        this->mainInterpreterMutex_ = new boost::mutex();
99        this->messageQueue_ = new TclThreadList<std::string>();
100
101        TclInterpreterBundle* newbundle = new TclInterpreterBundle();
102        newbundle->id_ = 0;
103        newbundle->interpreter_ = interpreter;
104        newbundle->lock_->lock();
105
[3313]106        {
[3318]107            boost::unique_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_);
108            this->interpreterBundles_[0] = newbundle;
[3313]109        }
[1505]110    }
111
[3318]112    /**
113        @brief Destructor
114    */
115    TclThreadManager::~TclThreadManager()
[1505]116    {
[3318]117        TclThreadManager::singletonPtr_s = 0;
[1505]118
[3318]119        delete this->interpreterBundlesMutex_;
[3326]120//        delete this->mainInterpreterMutex_; // <-- temporary disabled to avoid crash if a thread is still actively queriyng
[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    */
127    void TclThreadManager::update(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;
[3344]242        newbundle->interpreter_ = TclBind::createTclInterpreter();
[1505]243
[3318]244        // Initialize the new interpreter
245        try
[3313]246        {
[3318]247            std::string id_string = getConvertedValue<unsigned int, std::string>(id);
[3313]248
[3318]249            // Define the functions which are implemented in C++
250            newbundle->interpreter_->def("orxonox::execute",      TclThreadManager::tcl_execute,      Tcl::variadic());
251            newbundle->interpreter_->def("orxonox::crossexecute", TclThreadManager::tcl_crossexecute, Tcl::variadic());
252            newbundle->interpreter_->def("orxonox::query",        TclThreadManager::tcl_query,        Tcl::variadic());
253            newbundle->interpreter_->def("orxonox::crossquery",   TclThreadManager::tcl_crossquery,   Tcl::variadic());
254            newbundle->interpreter_->def("orxonox::running",      TclThreadManager::tcl_running);
[3313]255
[3318]256            // Create threadspecific shortcuts for the functions above
257            newbundle->interpreter_->def("execute",      TclThreadManager::tcl_execute,      Tcl::variadic());
258            newbundle->interpreter_->def("crossexecute", TclThreadManager::tcl_crossexecute, Tcl::variadic());
259            newbundle->interpreter_->eval("proc query       args     { orxonox::query " + id_string + " $args }");
260            newbundle->interpreter_->eval("proc crossquery {id args} { orxonox::crossquery " + id_string + " $id $args }");
[1505]261
[3318]262            // Define a variable containing the thread id
263            newbundle->interpreter_->eval("set id " + id_string);
[1505]264
[3318]265            // Use our own exit function to avoid shutting down the whole program instead of just the interpreter
266            newbundle->interpreter_->eval("rename exit tcl::exit");
267            newbundle->interpreter_->eval("proc exit {} { execute TclThreadManager destroy " + id_string + " }");
[1505]268
[3318]269            // Redefine some native functions
270//            newbundle->interpreter_->eval("rename while tcl::while");
271//            newbundle->interpreter_->eval("proc while {test command} { tcl::while {[uplevel 1 expr $test]} {uplevel 1 $command} }"); // (\"$test\" && [orxonox::running " + id + "]])
272//            newbundle->interpreter_->eval("rename for tcl::for");
273//            newbundle->interpreter_->eval("proc for {start test next command} { uplevel tcl::for \"$start\" \"$test\" \"$next\" \"$command\" }");
[3313]274        }
[3318]275        catch (const Tcl::tcl_error& e)
276        {   newbundle->interpreter_ = 0; COUT(1) << "Tcl error while creating Tcl-interpreter (" << id << "): " << e.what() << std::endl;   }
277        catch (const std::exception& e)
278        {   newbundle->interpreter_ = 0; COUT(1) << "Error while creating Tcl-interpreter (" << id << "): " << e.what() << std::endl;   }
279        catch (...)
280        {   newbundle->interpreter_ = 0; COUT(1) << "An error occurred while creating a new Tcl-interpreter (" << id << ")" << std::endl;   }
[1505]281
[3313]282        {
[3318]283            // Add the new bundle to the map
284            boost::unique_lock<boost::shared_mutex> lock(*TclThreadManager::getInstance().interpreterBundlesMutex_);
285            TclThreadManager::getInstance().interpreterBundles_[id] = newbundle;
[1505]286        }
287
[3318]288        return newbundle->interpreter_;
[3313]289    }
290
[3318]291    /**
292        @brief Stops and destroys a given Tcl-interpreter
293    */
294    void TclThreadManager::destroy(unsigned int id)
[3313]295    {
[3318]296        // TODO
297        // Not yet implemented
[3313]298    }
[1505]299
[3318]300    /**
301        @brief Sends a command to the queue of a given Tcl-interpreter
302        @param id The id of the target interpreter
303        @param command The command to be sent
304    */
305    void TclThreadManager::execute(unsigned int target_id, const std::string& command)
[3313]306    {
[3318]307        TclThreadManager::getInstance()._execute(target_id, command);
[3304]308    }
309
[3318]310    /**
311        @brief This function can be called from Tcl to execute a console command.
[3313]312
[3318]313        Commands which shall be executed are put into a queue and processed as soon as the
314        main thread feels ready to do so. The queue may also be full which results in a temporary
315        suspension of the calling thread until the queue gets ready again.
316    */
317    void TclThreadManager::tcl_execute(const Tcl::object& args)
[1505]318    {
[3318]319        TclThreadManager::getInstance()._execute(0, stripEnclosingBraces(args.get()));
[1505]320    }
321
[3318]322    /**
323        @brief This function can be called from Tcl to send a command to the queue of any interpreter.
324        @param target_id The id of the target thread
325    */
326    void TclThreadManager::tcl_crossexecute(int target_id, const Tcl::object& args)
[3313]327    {
[3318]328        TclThreadManager::getInstance()._execute(static_cast<unsigned int>(target_id), stripEnclosingBraces(args.get()));
[3313]329    }
[1505]330
[3318]331    /**
332        @brief Sends a command to the queue of a given Tcl-interpreter
333        @param id The id of the target interpreter
334        @param command The command to be sent
335    */
336    void TclThreadManager::_execute(unsigned int target_id, const std::string& command)
[1505]337    {
[3318]338        TclInterpreterBundle* bundle = this->getInterpreterBundle(target_id);
339        if (bundle)
340            bundle->queue_.push_back(command);
[1505]341    }
342
[3318]343
344    /**
345        @brief Sends a query to a given Tcl-interpreter and waits for the result
346        @param id The id of the target interpreter
347        @param command The command to be sent
348        @return The result of the command
349    */
350    std::string TclThreadManager::query(unsigned int target_id, const std::string& command)
[1505]351    {
[3318]352        return TclThreadManager::getInstance()._query(0, target_id, command);
[1505]353    }
354
[3318]355    /**
356        @brief This function can be called from Tcl to send a query to the main thread.
357        @param source_id The id of the calling thread
[3313]358
[3318]359        A query waits for the result of the command. This means, the calling thread will be blocked until
360        the main thread answers the query. In return, the main thread sends the result of the console
361        command back to Tcl.
362    */
363    std::string TclThreadManager::tcl_query(int source_id, const Tcl::object& args)
[3313]364    {
[3318]365        return TclThreadManager::getInstance()._query(static_cast<unsigned int>(source_id), 0, stripEnclosingBraces(args.get()), true);
[3313]366    }
[3307]367
[3318]368    /**
369        @brief This function can be called from Tcl to send a query to another thread.
370        @param source_id The id of the calling thread
371        @param target_id The id of the target thread
372    */
373    std::string TclThreadManager::tcl_crossquery(int source_id, int target_id, const Tcl::object& args)
[1505]374    {
[3318]375        return TclThreadManager::getInstance()._query(static_cast<unsigned int>(source_id), static_cast<unsigned int>(target_id), stripEnclosingBraces(args.get()));
[1505]376    }
377
[3318]378    /**
379        @brief This function performs a query to any Tcl interpreter
380        @param source_id The id of the calling thread
381        @param target_id The id of the target thread
382        @param command The command to send as a query
383        @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.
384    */
385    std::string TclThreadManager::_query(unsigned int source_id, unsigned int target_id, const std::string& command, bool bUseCommandExecutor)
[3313]386    {
[3318]387        TclInterpreterBundle* source_bundle = this->getInterpreterBundle(source_id);
388        TclInterpreterBundle* target_bundle = this->getInterpreterBundle(target_id);
389        std::string output;
[1505]390
[3318]391        if (source_bundle && target_bundle)
[3313]392        {
[3318]393            // 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)
394            // 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]395
[3318]396            if ((source_bundle->id_ == target_bundle->id_) || source_bundle->queriers_.is_in(target_bundle->id_))
[3313]397            {
[3318]398                // This query would lead to a deadlock - return with an error
399                this->error("Error: Circular query (" + this->dumpList(source_bundle->queriers_.getList()) + " " + getConvertedValue<unsigned int, std::string>(source_bundle->id_) \
400                            + " -> " + getConvertedValue<unsigned int, std::string>(target_bundle->id_) \
401                            + "), couldn't query Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(target_bundle->id_) \
402                            + " from other interpreter with ID " + getConvertedValue<unsigned int, std::string>(source_bundle->id_) + ".");
[3313]403            }
404            else
[3318]405            {
406                boost::unique_lock<boost::mutex> lock(target_bundle->mutex_, boost::try_to_lock);
407                boost::unique_lock<boost::mutex> mainlock(*this->mainInterpreterMutex_, boost::defer_lock);
[1505]408
[3318]409                if (!lock.owns_lock() && source_bundle->id_ != 0)
[1505]410                {
[3318]411                    // 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)
412                    if (target_bundle->id_ == 0)
[1505]413                    {
[3318]414                        // We're querying the main interpreter - use the main interpreter mutex to synchronize
415                        mainlock.lock();
416                        lock.lock();
417                    }
418                    else
419                    {
420                        // We're querying a threaded interpreter - no synchronization needed
421                        lock.lock();
422                    }
423                }
[1505]424
[3318]425                if (lock.owns_lock())
426                {
427                    // Now the mutex of target_bundle is also locked an we can update the querier list
428                    target_bundle->queriers_.insert(target_bundle->queriers_.getList().begin(), source_bundle->queriers_.getList().begin(), source_bundle->queriers_.getList().end());
429                    target_bundle->queriers_.push_back(source_bundle->id_);
[1505]430
[3318]431                    // Perform the query (note: this happens in the main thread because we need the returnvalue)
432                    if (target_bundle->id_ == 0 && bUseCommandExecutor)
[1505]433                    {
[3318]434                        // It's a query to the CommandExecutor
435                        this->debug("TclThread_query -> CE: " + command);
436                        if (!CommandExecutor::execute(command, false))
437                            this->error("Error: Can't execute command \"" + command + "\"!");
438
439                        if (CommandExecutor::getLastEvaluation().hasReturnvalue())
440                            output = CommandExecutor::getLastEvaluation().getReturnvalue().getString();
[1505]441                    }
442                    else
443                    {
[3318]444                        // It's a query to a Tcl interpreter
445                        this->debug("TclThread_query: " + command);
446
447                        output = this->eval(target_bundle, command);
[1505]448                    }
[3318]449
450                    // Clear the queriers list of the target
451                    target_bundle->queriers_.clear();
452
453                    // Unlock the mutex of the target_bundle
454                    lock.unlock();
455
456                    // Finally unlock the main interpreter lock if necessary
457                    if (mainlock.owns_lock())
458                        mainlock.unlock();
[3313]459                }
[3318]460                else
461                {
462                    // This happens if the main thread tries to query a busy interpreter
463                    // To avoid a lock of the main thread, we simply don't proceed with the query in this case
464                    this->error("Error: Couldn't query Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(target_bundle->id_) + ", interpreter is busy right now.");
465                }
466            }
[3307]467
[1505]468        }
[3318]469
[1505]470        return output;
471    }
472
[3318]473    /**
474        @brief This function can be called from Tcl to ask if the thread is still suposed to be running.
475        @param id The id of the thread in question
476    */
477    bool TclThreadManager::tcl_running(int id)
[1505]478    {
[3318]479        TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(static_cast<unsigned int>(id));
480        if (bundle)
481            return bundle->bRunning_;
482        else
483            return false;
484    }
485
486    /**
487        @brief Returns the interpreter bundle with the given id.
488        @param id The id of the interpreter
489        @return The interpreter or 0 if the id doesn't exist
490    */
491    TclInterpreterBundle* TclThreadManager::getInterpreterBundle(unsigned int id)
492    {
493        boost::shared_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_);
494
495        std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = this->interpreterBundles_.find(id);
496        if (it != this->interpreterBundles_.end())
[1505]497        {
[3318]498            return it->second;
[1505]499        }
[3318]500        else
[1505]501        {
[3318]502            this->error("Error: No Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(id) + " existing.");
503            return 0;
[1505]504        }
[3318]505    }
[1505]506
[3318]507    /**
508        @brief Returns a string containing all elements of a unsigned-integer-list separated by spaces.
509    */
510    std::string TclThreadManager::dumpList(const std::list<unsigned int>& list)
511    {
512        std::string output = "";
513        for (std::list<unsigned int>::const_iterator it = list.begin(); it != list.end(); ++it)
[1505]514        {
[3318]515            if (it != list.begin())
516                output += " ";
517
518            output += getConvertedValue<unsigned int, std::string>(*it);
[1505]519        }
[3318]520        return output;
[1505]521    }
522
[3318]523    /**
524        @brief Returns a list with the numbers of all existing Tcl-interpreters.
525
526        This function is used by the auto completion function.
527    */
[1505]528    std::list<unsigned int> TclThreadManager::getThreadList() const
529    {
[3318]530        boost::shared_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_);
531
[1505]532        std::list<unsigned int> threads;
533        for (std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = this->interpreterBundles_.begin(); it != this->interpreterBundles_.end(); ++it)
[3318]534            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)
535                threads.push_back(it->first);
[1505]536        return threads;
537    }
538
[3318]539    /**
540        @brief A helper function to print errors in a thread safe manner.
541    */
542    void TclThreadManager::error(const std::string& error)
[1505]543    {
[3318]544        this->messageQueue_->push_back("error " + error);
545    }
546
547    /**
548        @brief A helper function to print debug information in a thread safe manner.
549    */
550    void TclThreadManager::debug(const std::string& error)
551    {
552        this->messageQueue_->push_back("debug " + error);
553    }
554
555    /**
556        @brief Evaluates a Tcl command without throwing exceptions (which may rise problems on certain machines).
557        @return The Tcl return value
558
559        Errors are reported through the @ref error function.
560    */
561    std::string TclThreadManager::eval(TclInterpreterBundle* bundle, const std::string& command)
562    {
563        Tcl_Interp* interpreter = bundle->interpreter_->get();
564        int cc = Tcl_Eval(interpreter, command.c_str());
565
566        Tcl::details::result result(interpreter);
567
568        if (cc != TCL_OK)
[1505]569        {
[3318]570            this->error("Tcl error (execute, ID " + getConvertedValue<unsigned int, std::string>(bundle->id_) + "): " + static_cast<std::string>(result));
571            return "";
[1505]572        }
[3318]573        else
[1505]574        {
[3318]575            return result;
[1505]576        }
[3318]577    }
[1505]578
[3318]579    ////////////////
580    // The Thread //
581    ////////////////
582
583    /**
584        @brief The main function of the thread. Executes a Tcl command.
585        @param bundle The interpreter bundle containing all necessary variables
586        @param command the Command to execute
587    */
588    void tclThread(TclInterpreterBundle* bundle, std::string command)
589    {
590        TclThreadManager::getInstance().debug("TclThread_execute: " + command);
591
592        TclThreadManager::getInstance().eval(bundle, command);
593
594        bundle->lock_->unlock();
[1505]595    }
596}
Note: See TracBrowser for help on using the repository browser.