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

even better. i start to like it ;)

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