Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/consolecommands3/src/libraries/core/command/TclThreadManager.cc @ 7272

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

replaced the temporary names of all ConsoleCommand related classes and functions by their real names

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