Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/output/src/libraries/core/command/TclThreadManager.cc @ 8806

Last change on this file since 8806 was 8806, checked in by landauf, 13 years ago

Replaced COUT with orxout in core. Tried to set levels and contexts in a more or less useful way, but not really optimized.

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