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, 16 years ago

fixed some bugs

File size: 19.8 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include <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
45#define TCLTHREADMANAGER_MAX_QUEUE_LENGTH 100
46#define TCLTHREADMANAGER_MAX_CPU_USAGE 0.50
47
48namespace orxonox
49{
50//    ConsoleCommand(TclThreadManager, tclthread, AccessLevel::None, true);
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);
55//    ConsoleCommand(TclThreadManager, status,    AccessLevel::None, false);
56//    ConsoleCommand(TclThreadManager, dump,      AccessLevel::None, false);
57
58    TclThreadManager* instance = &TclThreadManager::getInstance();
59
60    TclThreadManager::TclThreadManager()
61    {
62        RegisterRootObject(TclThreadManager);
63
64        this->threadCounter_ = 0;
65        this->orxonoxInterpreterBundle_.id_ = 0;
66        this->orxonoxInterpreterBundle_.interpreter_ = TclBind::getInstance().getTclInterpreter();
67        this->threadID_ = boost::this_thread::get_id();
68    }
69
70    TclThreadManager& TclThreadManager::getInstance()
71    {
72        static TclThreadManager instance;
73        return instance;
74    }
75
76    unsigned int TclThreadManager::create()
77    {
78        boost::mutex::scoped_lock bundles_lock(TclThreadManager::getInstance().bundlesMutex_);
79        TclThreadManager::getInstance().threadCounter_++;
80        std::string name = getConvertedValue<unsigned int, std::string>(TclThreadManager::getInstance().threadCounter_);
81
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;
87        bundle->finished_ = true;
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_;
92    }
93
94    void TclThreadManager::destroy(unsigned int threadID)
95    {
96        TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(threadID);
97        if (bundle)
98        {
99            {
100                boost::mutex::scoped_lock running_lock(bundle->runningMutex_);
101                bundle->running_ = false;
102            }
103            while (true)
104            {
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();
128            }
129
130            COUT(0) << "Destroyed Tcl-interpreter with ID " << threadID << std::endl;
131        }
132    }
133
134    void TclThreadManager::execute(unsigned int threadID, const std::string& command)
135    {
136        TclThreadManager::getInstance().pushCommandToQueue(threadID, command);
137    }
138
139    std::string TclThreadManager::query(unsigned int threadID, const std::string& command)
140    {
141        return TclThreadManager::getInstance().evalQuery(TclThreadManager::getInstance().orxonoxInterpreterBundle_.id_, threadID, command);
142    }
143
144    void TclThreadManager::tcl_execute(Tcl::object const &args)
145    {
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);
149
150        TclThreadManager::getInstance().pushCommandToQueue(command);
151    }
152
153    std::string TclThreadManager::tcl_query(int querierID, Tcl::object const &args)
154    {
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);
158
159        return TclThreadManager::getInstance().evalQuery((unsigned int)querierID, command);
160    }
161
162    std::string TclThreadManager::tcl_crossquery(int querierID, int threadID, Tcl::object const &args)
163    {
164        std::string command = args.get();
165        while (command.size() >= 2 && command[0] == '{' && command[command.size() - 1] == '}')
166            command = command.substr(1, command.size() - 2);
167
168        return TclThreadManager::getInstance().evalQuery((unsigned int)querierID, (unsigned int)threadID, command);
169    }
170
171    bool TclThreadManager::tcl_running(int threadID)
172    {
173        TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle((unsigned int)threadID);
174        if (bundle)
175        {
176            boost::mutex::scoped_lock running_lock(bundle->runningMutex_);
177            return bundle->running_;
178        }
179        return false;
180    }
181
182    Tcl::interpreter* TclThreadManager::createNewTclInterpreter(const std::string& threadID)
183    {
184        Tcl::interpreter* i = 0;
185        i = new Tcl::interpreter(TclBind::getInstance().getTclLibPath());
186
187        try
188        {
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
202            i->eval("redef_puts");
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\" }");
208        }
209        catch (Tcl::tcl_error const &e)
210        {   COUT(1) << "Tcl error while creating Tcl-interpreter (" << threadID << "): " << e.what() << std::endl;   }
211        catch (std::exception const &e)
212        {   COUT(1) << "Error while creating Tcl-interpreter (" << threadID << "): " << e.what() << std::endl;   }
213
214        return i;
215    }
216
217    TclInterpreterBundle* TclThreadManager::getInterpreterBundle(unsigned int threadID)
218    {
219        boost::mutex::scoped_lock bundles_lock(this->bundlesMutex_);
220        std::map<unsigned int, TclInterpreterBundle*>::iterator it = this->interpreterBundles_.find(threadID);
221        if (it != this->interpreterBundles_.end())
222        {
223            return (*it).second;
224        }
225        else
226        {
227            this->error("Error: No Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(threadID) + " existing.");
228            return 0;
229        }
230    }
231
232    std::string TclThreadManager::dumpList(const std::list<unsigned int>& list)
233    {
234        std::string output = "";
235        for (std::list<unsigned int>::const_iterator it = list.begin(); it != list.end(); ++it)
236        {
237            if (it != list.begin())
238                output += " ";
239
240            output += getConvertedValue<unsigned int, std::string>(*it);
241        }
242        return output;
243    }
244
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
257    void TclThreadManager::pushCommandToQueue(const std::string& command)
258    {
259        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
260        while (this->orxonoxInterpreterBundle_.queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
261            this->fullQueueCondition_.wait(queue_lock);
262
263        this->orxonoxInterpreterBundle_.queue_.push_back(command);
264    }
265
266    void TclThreadManager::forceCommandToFrontOfQueue(const std::string& command)
267    {
268        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
269        this->orxonoxInterpreterBundle_.queue_.push_front(command);
270    }
271
272    std::string TclThreadManager::popCommandFromQueue()
273    {
274        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
275        std::string temp = this->orxonoxInterpreterBundle_.queue_.front();
276        this->orxonoxInterpreterBundle_.queue_.pop_front();
277        this->fullQueueCondition_.notify_one();
278        return temp;
279    }
280
281    bool TclThreadManager::queueIsEmpty()
282    {
283        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
284        return this->orxonoxInterpreterBundle_.queue_.empty();
285    }
286
287    void TclThreadManager::pushCommandToQueue(unsigned int threadID, const std::string& command)
288    {
289        TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
290        if (bundle)
291        {
292            boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
293            if (bundle->queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
294            {
295                this->error("Error: Queue of Tcl-interpreter " + getConvertedValue<unsigned int, std::string>(threadID) + " is full, couldn't add command.");
296                return;
297            }
298
299            bundle->queue_.push_back(command);
300        }
301    }
302
303    std::string TclThreadManager::popCommandFromQueue(unsigned int threadID)
304    {
305        TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
306        if (bundle)
307        {
308            boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
309            std::string temp = bundle->queue_.front();
310            bundle->queue_.pop_front();
311            return temp;
312        }
313        return "";
314    }
315
316    bool TclThreadManager::queueIsEmpty(unsigned int threadID)
317    {
318        TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
319        if (bundle)
320        {
321            boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
322            return bundle->queue_.empty();
323        }
324        return true;
325    }
326
327    bool TclThreadManager::updateQueriersList(TclInterpreterBundle* querier, TclInterpreterBundle* target)
328    {
329        if (querier == target)
330            return false;
331
332        boost::mutex::scoped_lock queriers_lock(target->queriersMutex_);
333
334        {
335            boost::mutex::scoped_lock queriers_lock(querier->queriersMutex_);
336            target->queriers_.insert(target->queriers_.end(), querier->queriers_.begin(), querier->queriers_.end());
337        }
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())
342        {
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_) + ".");
344            return false;
345        }
346
347        return true;
348    }
349
350    std::string TclThreadManager::evalQuery(unsigned int querierID, const std::string& command)
351    {
352        TclInterpreterBundle* querier = this->getInterpreterBundle(querierID);
353        std::string output = "";
354        if (querier)
355        {
356            if (this->updateQueriersList(querier, &this->orxonoxInterpreterBundle_))
357            {
358                boost::mutex::scoped_lock interpreter_lock(this->orxonoxInterpreterBundle_.interpreterMutex_);
359                this->orxonoxEvalCondition_.wait(interpreter_lock);
360
361                if (!CommandExecutor::execute(command, false))
362                    this->error("Error: Can't execute command \"" + command + "\"!");
363
364                if (CommandExecutor::getLastEvaluation().hasReturnvalue())
365                    output = CommandExecutor::getLastEvaluation().getReturnvalue().toString();
366            }
367
368            boost::mutex::scoped_lock queriers_lock(this->orxonoxInterpreterBundle_.queriersMutex_);
369            this->orxonoxInterpreterBundle_.queriers_.clear();
370        }
371        return output;
372    }
373
374    std::string TclThreadManager::evalQuery(unsigned int querierID, unsigned int threadID, const std::string& command)
375    {
376        TclInterpreterBundle* target = 0;
377        if (threadID)
378            target = this->getInterpreterBundle(threadID);
379        else
380            target = &this->orxonoxInterpreterBundle_;
381
382        std::string output = "";
383        if (target)
384        {
385            TclInterpreterBundle* querier = 0;
386            if (querierID)
387                querier = this->getInterpreterBundle(querierID);
388            else
389                querier = &this->orxonoxInterpreterBundle_;
390
391            if (querier)
392            {
393                if (this->updateQueriersList(querier, target))
394                {
395                    boost::mutex::scoped_lock interpreter_lock(target->interpreterMutex_, boost::defer_lock_t());
396                    bool successfullyLocked = false;
397                    try
398                    {
399                        if (querierID == 0 || std::find(querier->queriers_.begin(), querier->queriers_.end(), (unsigned int)0) != querier->queriers_.end())
400                            successfullyLocked = interpreter_lock.try_lock();
401                        else
402                        {
403                            while (!interpreter_lock.try_lock())
404                                boost::this_thread::yield();
405
406                            successfullyLocked = true;
407                        }
408                    } catch (...) {}
409
410                    if (successfullyLocked)
411                    {
412                        try
413                        {   output = (std::string)target->interpreter_->eval(command);   }
414                        catch (Tcl::tcl_error const &e)
415                        {   this->error("Tcl error: " + (std::string)e.what());   }
416                        catch (std::exception const &e)
417                        {   this->error("Error while executing Tcl: " + (std::string)e.what());   }
418                    }
419                    else
420                    {
421                        this->error("Error: Couldn't query Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(threadID) + ", interpreter is busy right now.");
422                    }
423                }
424
425                boost::mutex::scoped_lock queriers_lock(target->queriersMutex_);
426                target->queriers_.clear();
427            }
428        }
429        return output;
430    }
431
432    void TclThreadManager::tick(float dt)
433    {
434        {
435            this->orxonoxEvalCondition_.notify_one();
436            boost::this_thread::yield();
437        }
438
439        {
440            boost::mutex::scoped_lock bundles_lock(this->bundlesMutex_);
441            for (std::map<unsigned int, TclInterpreterBundle*>::iterator it = this->interpreterBundles_.begin(); it != this->interpreterBundles_.end(); ++it)
442            {
443                boost::mutex::scoped_lock queue_lock((*it).second->queueMutex_);
444                if (!(*it).second->queue_.empty())
445                {
446                    std::string command = (*it).second->queue_.front();
447                    (*it).second->queue_.pop_front();
448                    {
449                        boost::mutex::scoped_lock finished_lock((*it).second->finishedMutex_);
450                        (*it).second->finished_ = false;
451                    }
452                    boost::thread(boost::bind(&tclThread, (*it).second, command));
453                }
454            }
455        }
456
457        {
458            boost::mutex::scoped_lock interpreter_lock(this->orxonoxInterpreterBundle_.interpreterMutex_);
459            unsigned long maxtime = (unsigned long)(dt * 1000000 * TCLTHREADMANAGER_MAX_CPU_USAGE);
460            Ogre::Timer timer;
461            while (!this->queueIsEmpty())
462            {
463                CommandExecutor::execute(this->popCommandFromQueue(), false);
464                if (timer.getMicroseconds() > maxtime)
465                    break;
466            }
467        }
468    }
469
470    void tclThread(TclInterpreterBundle* interpreterBundle, std::string command)
471    {
472        boost::mutex::scoped_lock interpreter_lock(interpreterBundle->interpreterMutex_);
473        try
474        {
475            interpreterBundle->interpreter_->eval(command);
476        }
477        catch (Tcl::tcl_error const &e)
478        {
479            TclThreadManager::getInstance().error("Tcl (ID " + getConvertedValue<unsigned int, std::string>(interpreterBundle->id_) + ") error: " + e.what());
480        }
481        catch (std::exception const &e)
482        {
483            TclThreadManager::getInstance().error("Error while executing Tcl (ID " + getConvertedValue<unsigned int, std::string>(interpreterBundle->id_) + "): " + e.what());
484        }
485
486        boost::mutex::scoped_lock finished_lock(interpreterBundle->finishedMutex_);
487        interpreterBundle->finished_ = true;
488        interpreterBundle->finishedCondition_.notify_all();
489    }
490}
Note: See TracBrowser for help on using the repository browser.