Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/usability/src/libraries/core/command/TclThreadManager.cc @ 8030

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