Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/console/src/core/TclThreadManager.cc @ 1257

Last change on this file since 1257 was 1257, checked in by landauf, 17 years ago

fixed some bugs

File size: 19.8 KB
RevLine 
[1230]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 <iostream>
30#include <string>
31
32#include <boost/thread/thread.hpp>
33#include <boost/bind.hpp>
34
35#include <OgreTimer.h>
36
37#include "CoreIncludes.h"
38#include "ConsoleCommand.h"
39#include "CommandExecutor.h"
40#include "Debug.h"
41#include "TclBind.h"
42#include "TclThreadManager.h"
43#include "util/Convert.h"
44
[1257]45#define TCLTHREADMANAGER_MAX_QUEUE_LENGTH 100
[1255]46#define TCLTHREADMANAGER_MAX_CPU_USAGE 0.50
[1230]47
48namespace orxonox
49{
[1255]50//    ConsoleCommand(TclThreadManager, tclthread, AccessLevel::None, true);
[1230]51    ConsoleCommand(TclThreadManager, create,    AccessLevel::None, false);
52    ConsoleCommand(TclThreadManager, destroy,   AccessLevel::None, false);
53    ConsoleCommand(TclThreadManager, execute,   AccessLevel::None, false);
54    ConsoleCommand(TclThreadManager, query,     AccessLevel::None, false);
[1255]55//    ConsoleCommand(TclThreadManager, status,    AccessLevel::None, false);
56//    ConsoleCommand(TclThreadManager, dump,      AccessLevel::None, false);
[1230]57
58    TclThreadManager* instance = &TclThreadManager::getInstance();
59
60    TclThreadManager::TclThreadManager()
61    {
62        RegisterRootObject(TclThreadManager);
63
[1255]64        this->threadCounter_ = 0;
65        this->orxonoxInterpreterBundle_.id_ = 0;
66        this->orxonoxInterpreterBundle_.interpreter_ = TclBind::getInstance().getTclInterpreter();
[1257]67        this->threadID_ = boost::this_thread::get_id();
[1230]68    }
69
70    TclThreadManager& TclThreadManager::getInstance()
71    {
72        static TclThreadManager instance;
73        return instance;
74    }
75
[1255]76    unsigned int TclThreadManager::create()
[1230]77    {
[1257]78        boost::mutex::scoped_lock bundles_lock(TclThreadManager::getInstance().bundlesMutex_);
[1255]79        TclThreadManager::getInstance().threadCounter_++;
80        std::string name = getConvertedValue<unsigned int, std::string>(TclThreadManager::getInstance().threadCounter_);
[1230]81
[1255]82        TclInterpreterBundle* bundle = new TclInterpreterBundle;
83        bundle->id_ = TclThreadManager::getInstance().threadCounter_;
84        bundle->interpreter_ = TclThreadManager::getInstance().createNewTclInterpreter(name);
85        bundle->interpreterName_ = name;
86        bundle->running_ = true;
[1257]87        bundle->finished_ = true;
[1255]88
89        TclThreadManager::getInstance().interpreterBundles_[TclThreadManager::getInstance().threadCounter_] = bundle;
90        COUT(0) << "Created new Tcl-interpreter with ID " << TclThreadManager::getInstance().threadCounter_ << std::endl;
91        return TclThreadManager::getInstance().threadCounter_;
[1230]92    }
93
94    void TclThreadManager::destroy(unsigned int threadID)
95    {
[1255]96        TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(threadID);
97        if (bundle)
[1230]98        {
[1255]99            {
[1257]100                boost::mutex::scoped_lock running_lock(bundle->runningMutex_);
[1255]101                bundle->running_ = false;
102            }
[1257]103            while (true)
[1255]104            {
[1257]105                {
106                    boost::mutex::scoped_lock finished_lock(bundle->finishedMutex_);
107                    if (bundle->finished_)
108                    {
109                        boost::mutex::scoped_lock bundles_lock(TclThreadManager::getInstance().bundlesMutex_);
110                        boost::mutex::scoped_lock interpreter_lock(bundle->interpreterMutex_, boost::defer_lock_t());
111                        try
112                        {
113                            while (!interpreter_lock.try_lock())
114                            {
115                                TclThreadManager::getInstance().orxonoxEvalCondition_.notify_one();
116                                boost::this_thread::yield();
117                            }
118                        } catch (...) {}
119                        delete bundle->interpreter_;
120                        delete bundle;
121                        TclThreadManager::getInstance().interpreterBundles_.erase(threadID);
122                        break;
123                    }
124                }
125
126                TclThreadManager::getInstance().orxonoxEvalCondition_.notify_one();
127                boost::this_thread::yield();
[1255]128            }
[1257]129
130            COUT(0) << "Destroyed Tcl-interpreter with ID " << threadID << std::endl;
[1230]131        }
132    }
133
134    void TclThreadManager::execute(unsigned int threadID, const std::string& command)
135    {
[1255]136        TclThreadManager::getInstance().pushCommandToQueue(threadID, command);
[1230]137    }
138
139    std::string TclThreadManager::query(unsigned int threadID, const std::string& command)
140    {
[1255]141        return TclThreadManager::getInstance().evalQuery(TclThreadManager::getInstance().orxonoxInterpreterBundle_.id_, threadID, command);
[1230]142    }
143
[1255]144    void TclThreadManager::tcl_execute(Tcl::object const &args)
[1247]145    {
[1255]146        std::string command = args.get();
147        while (command.size() >= 2 && command[0] == '{' && command[command.size() - 1] == '}')
148            command = command.substr(1, command.size() - 2);
[1247]149
[1255]150        TclThreadManager::getInstance().pushCommandToQueue(command);
[1247]151    }
152
[1255]153    std::string TclThreadManager::tcl_query(int querierID, Tcl::object const &args)
[1247]154    {
[1255]155        std::string command = args.get();
156        while (command.size() >= 2 && command[0] == '{' && command[command.size() - 1] == '}')
157            command = command.substr(1, command.size() - 2);
[1247]158
[1255]159        return TclThreadManager::getInstance().evalQuery((unsigned int)querierID, command);
[1247]160    }
161
[1255]162    std::string TclThreadManager::tcl_crossquery(int querierID, int threadID, Tcl::object const &args)
[1230]163    {
164        std::string command = args.get();
[1255]165        while (command.size() >= 2 && command[0] == '{' && command[command.size() - 1] == '}')
[1230]166            command = command.substr(1, command.size() - 2);
167
[1255]168        return TclThreadManager::getInstance().evalQuery((unsigned int)querierID, (unsigned int)threadID, command);
[1230]169    }
170
[1255]171    bool TclThreadManager::tcl_running(int threadID)
[1230]172    {
[1255]173        TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle((unsigned int)threadID);
174        if (bundle)
[1247]175        {
[1257]176            boost::mutex::scoped_lock running_lock(bundle->runningMutex_);
[1255]177            return bundle->running_;
[1247]178        }
[1255]179        return false;
[1230]180    }
181
[1255]182    Tcl::interpreter* TclThreadManager::createNewTclInterpreter(const std::string& threadID)
[1230]183    {
[1247]184        Tcl::interpreter* i = 0;
185        i = new Tcl::interpreter(TclBind::getInstance().getTclLibPath());
[1230]186
[1247]187        try
188        {
[1255]189            i->def("orxonox::query", TclThreadManager::tcl_query, Tcl::variadic());
190            i->def("orxonox::crossquery", TclThreadManager::tcl_crossquery, Tcl::variadic());
191            i->def("orxonox::execute", TclThreadManager::tcl_execute, Tcl::variadic());
192            i->def("orxonox::running", TclThreadManager::tcl_running);
193
194            i->def("execute", TclThreadManager::tcl_execute, Tcl::variadic());
195            i->eval("proc query args { orxonox::query " + threadID + " $args }");
196            i->eval("proc crossquery {id args} { orxonox::crossquery " + threadID + " $id $args }");
197            i->eval("set id " + threadID);
198
199            i->eval("rename exit tcl::exit");
200            i->eval("proc exit {} { orxonox TclThreadManager destroy " + threadID + " }");
201
[1247]202            i->eval("redef_puts");
[1255]203
204            i->eval("rename while tcl::while");
205            i->eval("proc while {test command} { tcl::while {$test && [orxonox::running " + threadID + "]} \"$command\" }");
206//            i->eval("rename for tcl::for");
207//            i->eval("proc for {start test next command} { uplevel tcl::for \"$start\" \"$test\" \"$next\" \"$command\" }");
[1247]208        }
209        catch (Tcl::tcl_error const &e)
[1255]210        {   COUT(1) << "Tcl error while creating Tcl-interpreter (" << threadID << "): " << e.what() << std::endl;   }
[1247]211        catch (std::exception const &e)
[1255]212        {   COUT(1) << "Error while creating Tcl-interpreter (" << threadID << "): " << e.what() << std::endl;   }
[1230]213
214        return i;
215    }
216
[1255]217    TclInterpreterBundle* TclThreadManager::getInterpreterBundle(unsigned int threadID)
[1230]218    {
[1257]219        boost::mutex::scoped_lock bundles_lock(this->bundlesMutex_);
[1255]220        std::map<unsigned int, TclInterpreterBundle*>::iterator it = this->interpreterBundles_.find(threadID);
221        if (it != this->interpreterBundles_.end())
[1230]222        {
[1255]223            return (*it).second;
[1230]224        }
[1255]225        else
[1230]226        {
[1257]227            this->error("Error: No Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(threadID) + " existing.");
[1255]228            return 0;
[1230]229        }
230    }
231
[1255]232    std::string TclThreadManager::dumpList(const std::list<unsigned int>& list)
[1230]233    {
[1255]234        std::string output = "";
235        for (std::list<unsigned int>::const_iterator it = list.begin(); it != list.end(); ++it)
[1230]236        {
[1255]237            if (it != list.begin())
238                output += " ";
[1230]239
[1255]240            output += getConvertedValue<unsigned int, std::string>(*it);
[1230]241        }
[1255]242        return output;
[1230]243    }
244
[1257]245    void TclThreadManager::error(const std::string& error)
246    {
247        if (boost::this_thread::get_id() != this->threadID_)
248        {
249            boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
250            if (this->orxonoxInterpreterBundle_.queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
251                return;
252        }
253
254        this->forceCommandToFrontOfQueue("error " + error);
255    }
256
[1255]257    void TclThreadManager::pushCommandToQueue(const std::string& command)
[1247]258    {
[1257]259        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
[1255]260        while (this->orxonoxInterpreterBundle_.queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
[1257]261            this->fullQueueCondition_.wait(queue_lock);
[1255]262
263        this->orxonoxInterpreterBundle_.queue_.push_back(command);
[1247]264    }
265
[1255]266    void TclThreadManager::forceCommandToFrontOfQueue(const std::string& command)
[1247]267    {
[1257]268        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
[1255]269        this->orxonoxInterpreterBundle_.queue_.push_front(command);
[1247]270    }
271
[1255]272    std::string TclThreadManager::popCommandFromQueue()
[1230]273    {
[1257]274        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
[1255]275        std::string temp = this->orxonoxInterpreterBundle_.queue_.front();
276        this->orxonoxInterpreterBundle_.queue_.pop_front();
277        this->fullQueueCondition_.notify_one();
278        return temp;
[1230]279    }
280
[1255]281    bool TclThreadManager::queueIsEmpty()
[1230]282    {
[1257]283        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
[1255]284        return this->orxonoxInterpreterBundle_.queue_.empty();
[1230]285    }
286
[1255]287    void TclThreadManager::pushCommandToQueue(unsigned int threadID, const std::string& command)
[1230]288    {
[1255]289        TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
290        if (bundle)
291        {
[1257]292            boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
[1255]293            if (bundle->queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
294            {
[1257]295                this->error("Error: Queue of Tcl-interpreter " + getConvertedValue<unsigned int, std::string>(threadID) + " is full, couldn't add command.");
[1255]296                return;
297            }
[1230]298
[1255]299            bundle->queue_.push_back(command);
300        }
[1230]301    }
302
[1255]303    std::string TclThreadManager::popCommandFromQueue(unsigned int threadID)
[1230]304    {
[1255]305        TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
306        if (bundle)
[1230]307        {
[1257]308            boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
[1255]309            std::string temp = bundle->queue_.front();
310            bundle->queue_.pop_front();
311            return temp;
[1230]312        }
[1255]313        return "";
[1230]314    }
315
[1255]316    bool TclThreadManager::queueIsEmpty(unsigned int threadID)
[1230]317    {
[1255]318        TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
319        if (bundle)
[1230]320        {
[1257]321            boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
[1255]322            return bundle->queue_.empty();
[1230]323        }
[1255]324        return true;
[1230]325    }
326
[1255]327    bool TclThreadManager::updateQueriersList(TclInterpreterBundle* querier, TclInterpreterBundle* target)
[1230]328    {
[1255]329        if (querier == target)
330            return false;
[1230]331
[1257]332        boost::mutex::scoped_lock queriers_lock(target->queriersMutex_);
[1255]333
[1230]334        {
[1257]335            boost::mutex::scoped_lock queriers_lock(querier->queriersMutex_);
[1255]336            target->queriers_.insert(target->queriers_.end(), querier->queriers_.begin(), querier->queriers_.end());
[1230]337        }
[1255]338
339        target->queriers_.insert(target->queriers_.end(), querier->id_);
340
341        if (std::find(target->queriers_.begin(), target->queriers_.end(), target->id_) != target->queriers_.end())
[1230]342        {
[1257]343            this->error("Error: Circular query (" + this->dumpList(target->queriers_) + " -> " + getConvertedValue<unsigned int, std::string>(target->id_) + "), couldn't query Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(target->id_) + " from other interpreter with ID " + getConvertedValue<unsigned int, std::string>(querier->id_) + ".");
[1255]344            return false;
[1230]345        }
[1255]346
347        return true;
[1230]348    }
349
[1255]350    std::string TclThreadManager::evalQuery(unsigned int querierID, const std::string& command)
[1230]351    {
[1255]352        TclInterpreterBundle* querier = this->getInterpreterBundle(querierID);
353        std::string output = "";
354        if (querier)
355        {
356            if (this->updateQueriersList(querier, &this->orxonoxInterpreterBundle_))
357            {
[1257]358                boost::mutex::scoped_lock interpreter_lock(this->orxonoxInterpreterBundle_.interpreterMutex_);
359                this->orxonoxEvalCondition_.wait(interpreter_lock);
[1230]360
[1255]361                if (!CommandExecutor::execute(command, false))
[1257]362                    this->error("Error: Can't execute command \"" + command + "\"!");
[1230]363
[1255]364                if (CommandExecutor::getLastEvaluation().hasReturnvalue())
365                    output = CommandExecutor::getLastEvaluation().getReturnvalue().toString();
366            }
[1230]367
[1257]368            boost::mutex::scoped_lock queriers_lock(this->orxonoxInterpreterBundle_.queriersMutex_);
[1255]369            this->orxonoxInterpreterBundle_.queriers_.clear();
370        }
371        return output;
[1230]372    }
373
[1255]374    std::string TclThreadManager::evalQuery(unsigned int querierID, unsigned int threadID, const std::string& command)
[1230]375    {
[1257]376        TclInterpreterBundle* target = 0;
377        if (threadID)
378            target = this->getInterpreterBundle(threadID);
379        else
380            target = &this->orxonoxInterpreterBundle_;
381
[1255]382        std::string output = "";
383        if (target)
[1247]384        {
[1255]385            TclInterpreterBundle* querier = 0;
386            if (querierID)
387                querier = this->getInterpreterBundle(querierID);
388            else
389                querier = &this->orxonoxInterpreterBundle_;
[1230]390
[1255]391            if (querier)
[1230]392            {
[1255]393                if (this->updateQueriersList(querier, target))
[1230]394                {
[1257]395                    boost::mutex::scoped_lock interpreter_lock(target->interpreterMutex_, boost::defer_lock_t());
[1255]396                    bool successfullyLocked = false;
[1247]397                    try
398                    {
[1255]399                        if (querierID == 0 || std::find(querier->queriers_.begin(), querier->queriers_.end(), (unsigned int)0) != querier->queriers_.end())
[1257]400                            successfullyLocked = interpreter_lock.try_lock();
[1255]401                        else
402                        {
[1257]403                            while (!interpreter_lock.try_lock())
[1255]404                                boost::this_thread::yield();
405
406                            successfullyLocked = true;
407                        }
408                    } catch (...) {}
409
410                    if (successfullyLocked)
[1247]411                    {
[1255]412                        try
413                        {   output = (std::string)target->interpreter_->eval(command);   }
414                        catch (Tcl::tcl_error const &e)
[1257]415                        {   this->error("Tcl error: " + (std::string)e.what());   }
[1255]416                        catch (std::exception const &e)
[1257]417                        {   this->error("Error while executing Tcl: " + (std::string)e.what());   }
[1247]418                    }
[1255]419                    else
[1247]420                    {
[1257]421                        this->error("Error: Couldn't query Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(threadID) + ", interpreter is busy right now.");
[1247]422                    }
[1230]423                }
[1255]424
[1257]425                boost::mutex::scoped_lock queriers_lock(target->queriersMutex_);
[1255]426                target->queriers_.clear();
[1230]427            }
428        }
[1255]429        return output;
[1230]430    }
431
432    void TclThreadManager::tick(float dt)
433    {
434        {
[1255]435            this->orxonoxEvalCondition_.notify_one();
436            boost::this_thread::yield();
[1230]437        }
438
439        {
[1257]440            boost::mutex::scoped_lock bundles_lock(this->bundlesMutex_);
[1255]441            for (std::map<unsigned int, TclInterpreterBundle*>::iterator it = this->interpreterBundles_.begin(); it != this->interpreterBundles_.end(); ++it)
[1230]442            {
[1257]443                boost::mutex::scoped_lock queue_lock((*it).second->queueMutex_);
[1255]444                if (!(*it).second->queue_.empty())
[1230]445                {
[1255]446                    std::string command = (*it).second->queue_.front();
447                    (*it).second->queue_.pop_front();
[1247]448                    {
[1257]449                        boost::mutex::scoped_lock finished_lock((*it).second->finishedMutex_);
[1255]450                        (*it).second->finished_ = false;
[1247]451                    }
[1255]452                    boost::thread(boost::bind(&tclThread, (*it).second, command));
[1230]453                }
[1247]454            }
455        }
456
457        {
[1257]458            boost::mutex::scoped_lock interpreter_lock(this->orxonoxInterpreterBundle_.interpreterMutex_);
[1255]459            unsigned long maxtime = (unsigned long)(dt * 1000000 * TCLTHREADMANAGER_MAX_CPU_USAGE);
460            Ogre::Timer timer;
461            while (!this->queueIsEmpty())
[1247]462            {
[1255]463                CommandExecutor::execute(this->popCommandFromQueue(), false);
464                if (timer.getMicroseconds() > maxtime)
465                    break;
[1230]466            }
[1255]467        }
468    }
[1230]469
[1255]470    void tclThread(TclInterpreterBundle* interpreterBundle, std::string command)
471    {
[1257]472        boost::mutex::scoped_lock interpreter_lock(interpreterBundle->interpreterMutex_);
[1255]473        try
474        {
475            interpreterBundle->interpreter_->eval(command);
476        }
477        catch (Tcl::tcl_error const &e)
478        {
[1257]479            TclThreadManager::getInstance().error("Tcl (ID " + getConvertedValue<unsigned int, std::string>(interpreterBundle->id_) + ") error: " + e.what());
[1255]480        }
481        catch (std::exception const &e)
482        {
[1257]483            TclThreadManager::getInstance().error("Error while executing Tcl (ID " + getConvertedValue<unsigned int, std::string>(interpreterBundle->id_) + "): " + e.what());
[1255]484        }
485
[1257]486        boost::mutex::scoped_lock finished_lock(interpreterBundle->finishedMutex_);
[1255]487        interpreterBundle->finished_ = true;
488        interpreterBundle->finishedCondition_.notify_all();
[1230]489    }
490}
Note: See TracBrowser for help on using the repository browser.