Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/command/TclThreadManager.cc @ 7401

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

merged doc branch back to trunk

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