Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1267 was 1267, checked in by landauf, 16 years ago

telnet remote control works

type "source remote.tcl" into the ingame shell
connect with tcl to port 2560
login with "orxonox rocks"
type any command you like… it will be executed in orxonox ;)
of course server-admins will change the password (and the port)

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 {[uplevel 1 expr $test]} {uplevel 1 $command} }"); // (\"$test\" && [orxonox::running " + threadID + "]])
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.