Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/pickup/src/libraries/core/TclThreadManager.cc @ 6227

Last change on this file since 6227 was 5935, checked in by dafrick, 15 years ago

Hopefully merged trunk successfully into pickup branch.

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