Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/doc/src/libraries/core/command/TclThreadManager.cc @ 7297

Last change on this file since 7297 was 7297, checked in by landauf, 14 years ago

fixed lots of Doxygen warnings

Note: Doxygen prints a warning if only a part of the parameters of a function are documented.

Added documentation for missing parameters (as good as I could), removed documentation of obsolete parameters and fixed names of renamed parameters.
Some parameters are tagged with "FIXME", please replace this with an appropriate documentation if you know what it does.

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