Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

even better. i start to like it ;)

File size: 19.9 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)
[1258]251            {
252                boost::this_thread::yield();
[1257]253                return;
[1258]254            }
[1257]255        }
256
257        this->forceCommandToFrontOfQueue("error " + error);
258    }
259
[1255]260    void TclThreadManager::pushCommandToQueue(const std::string& command)
[1247]261    {
[1257]262        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
[1255]263        while (this->orxonoxInterpreterBundle_.queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
[1257]264            this->fullQueueCondition_.wait(queue_lock);
[1255]265
266        this->orxonoxInterpreterBundle_.queue_.push_back(command);
[1247]267    }
268
[1255]269    void TclThreadManager::forceCommandToFrontOfQueue(const std::string& command)
[1247]270    {
[1257]271        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
[1255]272        this->orxonoxInterpreterBundle_.queue_.push_front(command);
[1247]273    }
274
[1255]275    std::string TclThreadManager::popCommandFromQueue()
[1230]276    {
[1257]277        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
[1255]278        std::string temp = this->orxonoxInterpreterBundle_.queue_.front();
279        this->orxonoxInterpreterBundle_.queue_.pop_front();
280        this->fullQueueCondition_.notify_one();
281        return temp;
[1230]282    }
283
[1255]284    bool TclThreadManager::queueIsEmpty()
[1230]285    {
[1257]286        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
[1255]287        return this->orxonoxInterpreterBundle_.queue_.empty();
[1230]288    }
289
[1255]290    void TclThreadManager::pushCommandToQueue(unsigned int threadID, const std::string& command)
[1230]291    {
[1255]292        TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
293        if (bundle)
294        {
[1257]295            boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
[1255]296            if (bundle->queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
297            {
[1257]298                this->error("Error: Queue of Tcl-interpreter " + getConvertedValue<unsigned int, std::string>(threadID) + " is full, couldn't add command.");
[1255]299                return;
300            }
[1230]301
[1255]302            bundle->queue_.push_back(command);
303        }
[1230]304    }
305
[1255]306    std::string TclThreadManager::popCommandFromQueue(unsigned int threadID)
[1230]307    {
[1255]308        TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
309        if (bundle)
[1230]310        {
[1257]311            boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
[1255]312            std::string temp = bundle->queue_.front();
313            bundle->queue_.pop_front();
314            return temp;
[1230]315        }
[1255]316        return "";
[1230]317    }
318
[1255]319    bool TclThreadManager::queueIsEmpty(unsigned int threadID)
[1230]320    {
[1255]321        TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
322        if (bundle)
[1230]323        {
[1257]324            boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
[1255]325            return bundle->queue_.empty();
[1230]326        }
[1255]327        return true;
[1230]328    }
329
[1255]330    bool TclThreadManager::updateQueriersList(TclInterpreterBundle* querier, TclInterpreterBundle* target)
[1230]331    {
[1255]332        if (querier == target)
333            return false;
[1230]334
[1257]335        boost::mutex::scoped_lock queriers_lock(target->queriersMutex_);
[1255]336
[1230]337        {
[1257]338            boost::mutex::scoped_lock queriers_lock(querier->queriersMutex_);
[1255]339            target->queriers_.insert(target->queriers_.end(), querier->queriers_.begin(), querier->queriers_.end());
[1230]340        }
[1255]341
342        target->queriers_.insert(target->queriers_.end(), querier->id_);
343
344        if (std::find(target->queriers_.begin(), target->queriers_.end(), target->id_) != target->queriers_.end())
[1230]345        {
[1257]346            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]347            return false;
[1230]348        }
[1255]349
350        return true;
[1230]351    }
352
[1255]353    std::string TclThreadManager::evalQuery(unsigned int querierID, const std::string& command)
[1230]354    {
[1255]355        TclInterpreterBundle* querier = this->getInterpreterBundle(querierID);
356        std::string output = "";
357        if (querier)
358        {
359            if (this->updateQueriersList(querier, &this->orxonoxInterpreterBundle_))
360            {
[1257]361                boost::mutex::scoped_lock interpreter_lock(this->orxonoxInterpreterBundle_.interpreterMutex_);
362                this->orxonoxEvalCondition_.wait(interpreter_lock);
[1230]363
[1255]364                if (!CommandExecutor::execute(command, false))
[1257]365                    this->error("Error: Can't execute command \"" + command + "\"!");
[1230]366
[1255]367                if (CommandExecutor::getLastEvaluation().hasReturnvalue())
368                    output = CommandExecutor::getLastEvaluation().getReturnvalue().toString();
369            }
[1230]370
[1257]371            boost::mutex::scoped_lock queriers_lock(this->orxonoxInterpreterBundle_.queriersMutex_);
[1255]372            this->orxonoxInterpreterBundle_.queriers_.clear();
373        }
374        return output;
[1230]375    }
376
[1255]377    std::string TclThreadManager::evalQuery(unsigned int querierID, unsigned int threadID, const std::string& command)
[1230]378    {
[1257]379        TclInterpreterBundle* target = 0;
380        if (threadID)
381            target = this->getInterpreterBundle(threadID);
382        else
383            target = &this->orxonoxInterpreterBundle_;
384
[1255]385        std::string output = "";
386        if (target)
[1247]387        {
[1255]388            TclInterpreterBundle* querier = 0;
389            if (querierID)
390                querier = this->getInterpreterBundle(querierID);
391            else
392                querier = &this->orxonoxInterpreterBundle_;
[1230]393
[1255]394            if (querier)
[1230]395            {
[1255]396                if (this->updateQueriersList(querier, target))
[1230]397                {
[1257]398                    boost::mutex::scoped_lock interpreter_lock(target->interpreterMutex_, boost::defer_lock_t());
[1255]399                    bool successfullyLocked = false;
[1247]400                    try
401                    {
[1255]402                        if (querierID == 0 || std::find(querier->queriers_.begin(), querier->queriers_.end(), (unsigned int)0) != querier->queriers_.end())
[1257]403                            successfullyLocked = interpreter_lock.try_lock();
[1255]404                        else
405                        {
[1257]406                            while (!interpreter_lock.try_lock())
[1255]407                                boost::this_thread::yield();
408
409                            successfullyLocked = true;
410                        }
411                    } catch (...) {}
412
413                    if (successfullyLocked)
[1247]414                    {
[1255]415                        try
416                        {   output = (std::string)target->interpreter_->eval(command);   }
417                        catch (Tcl::tcl_error const &e)
[1257]418                        {   this->error("Tcl error: " + (std::string)e.what());   }
[1255]419                        catch (std::exception const &e)
[1257]420                        {   this->error("Error while executing Tcl: " + (std::string)e.what());   }
[1247]421                    }
[1255]422                    else
[1247]423                    {
[1257]424                        this->error("Error: Couldn't query Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(threadID) + ", interpreter is busy right now.");
[1247]425                    }
[1230]426                }
[1255]427
[1257]428                boost::mutex::scoped_lock queriers_lock(target->queriersMutex_);
[1255]429                target->queriers_.clear();
[1230]430            }
431        }
[1255]432        return output;
[1230]433    }
434
435    void TclThreadManager::tick(float dt)
436    {
437        {
[1255]438            this->orxonoxEvalCondition_.notify_one();
439            boost::this_thread::yield();
[1230]440        }
441
442        {
[1257]443            boost::mutex::scoped_lock bundles_lock(this->bundlesMutex_);
[1255]444            for (std::map<unsigned int, TclInterpreterBundle*>::iterator it = this->interpreterBundles_.begin(); it != this->interpreterBundles_.end(); ++it)
[1230]445            {
[1257]446                boost::mutex::scoped_lock queue_lock((*it).second->queueMutex_);
[1255]447                if (!(*it).second->queue_.empty())
[1230]448                {
[1255]449                    std::string command = (*it).second->queue_.front();
450                    (*it).second->queue_.pop_front();
[1247]451                    {
[1257]452                        boost::mutex::scoped_lock finished_lock((*it).second->finishedMutex_);
[1255]453                        (*it).second->finished_ = false;
[1247]454                    }
[1255]455                    boost::thread(boost::bind(&tclThread, (*it).second, command));
[1230]456                }
[1247]457            }
458        }
459
460        {
[1257]461            boost::mutex::scoped_lock interpreter_lock(this->orxonoxInterpreterBundle_.interpreterMutex_);
[1255]462            unsigned long maxtime = (unsigned long)(dt * 1000000 * TCLTHREADMANAGER_MAX_CPU_USAGE);
463            Ogre::Timer timer;
464            while (!this->queueIsEmpty())
[1247]465            {
[1255]466                CommandExecutor::execute(this->popCommandFromQueue(), false);
467                if (timer.getMicroseconds() > maxtime)
468                    break;
[1230]469            }
[1255]470        }
471    }
[1230]472
[1255]473    void tclThread(TclInterpreterBundle* interpreterBundle, std::string command)
474    {
[1257]475        boost::mutex::scoped_lock interpreter_lock(interpreterBundle->interpreterMutex_);
[1255]476        try
477        {
478            interpreterBundle->interpreter_->eval(command);
479        }
480        catch (Tcl::tcl_error const &e)
481        {
[1257]482            TclThreadManager::getInstance().error("Tcl (ID " + getConvertedValue<unsigned int, std::string>(interpreterBundle->id_) + ") error: " + e.what());
[1255]483        }
484        catch (std::exception const &e)
485        {
[1257]486            TclThreadManager::getInstance().error("Error while executing Tcl (ID " + getConvertedValue<unsigned int, std::string>(interpreterBundle->id_) + "): " + e.what());
[1255]487        }
488
[1257]489        boost::mutex::scoped_lock finished_lock(interpreterBundle->finishedMutex_);
[1255]490        interpreterBundle->finished_ = true;
491        interpreterBundle->finishedCondition_.notify_all();
[1230]492    }
493}
Note: See TracBrowser for help on using the repository browser.