Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

several changes in TclThreadManager but couldn't yet fix the bug

File size: 21.4 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 TCLTHREAD_MAX_QUEUE_LENGTH 1024
46#define TCLTHREAD_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->IDcount_ = 0;
65        this->isReady_ = false;
66        this->isQuerying_ = false;
67        this->queryID_ = 0;
68    }
69
70    TclThreadManager& TclThreadManager::getInstance()
71    {
72        static TclThreadManager instance;
73        return instance;
74    }
75
76    void TclThreadManager::tclthread(unsigned int threadID, const std::string& command)
77    {
78        TclThreadManager::execute(threadID, command);
79    }
80
81    unsigned int TclThreadManager::create()
82    {
83        if (TclThreadManager::getInstance().createTclThread())
84        {
85            COUT(0) << "Created new Tcl-thread with ID " << TclThreadManager::getInstance().IDcount_ << "." << std::endl;
86            return TclThreadManager::getInstance().IDcount_;
87        }
88        return 0;
89    }
90
91    void TclThreadManager::destroy(unsigned int threadID)
92    {
93        if (TclThreadManager::getInstance().destroyTclThread(threadID))
94        {
95            COUT(0) << "Destroyed Tcl-thread with ID " << threadID << "." << std::endl;
96        }
97    }
98
99    void TclThreadManager::execute(unsigned int threadID, const std::string& command)
100    {
101        TclThreadManager::getInstance().pushCommandBack(threadID, command);
102    }
103
104    std::string TclThreadManager::query(unsigned int threadID, const std::string& command)
105    {
106        return TclThreadManager::getInstance().eval(threadID, command);
107    }
108
109    void TclThreadManager::status()
110    {
111        COUT(0) << "Thread ID" << '\t' << "Queue size" << '\t' << "State" << std::endl;
112
113        std::string output = "Orxonox";
114        output += "\t\t";
115        {
116            // mutex orxqueue
117            boost::mutex::scoped_lock lock(TclThreadManager::getInstance().orxonoxQueueMutex_);
118            output += getConvertedValue<unsigned int, std::string>(TclThreadManager::getInstance().orxonoxQueue_.size());
119        }
120        output += "\t\t";
121        {
122            // mutex orxstate
123            boost::mutex::scoped_lock lock(TclThreadManager::getInstance().orxonoxStateMutex_);
124            if (TclThreadManager::getInstance().isReady_)
125                output += "ready";
126            else
127                output += "busy";
128        }
129        COUT(0) << output << std::endl;
130
131        // mutex threads
132        boost::mutex::scoped_lock lock(TclThreadManager::getInstance().threadsMutex_);
133        for (std::map<unsigned int, TclThread*>::const_iterator it = TclThreadManager::getInstance().threads_.begin(); it != TclThreadManager::getInstance().threads_.end(); ++it)
134        {
135            std::string output = getConvertedValue<unsigned int, std::string>((*it).first);
136            output += "\t\t";
137            {
138                boost::mutex::scoped_lock lock(TclThreadManager::getInstance().threadQueuesMutex_);
139                std::map<unsigned int, std::pair<std::queue<std::string>, boost::condition*> >::const_iterator it2 = TclThreadManager::getInstance().threadQueues_.find((*it).first);
140                if (it2 != TclThreadManager::getInstance().threadQueues_.end())
141                    output += getConvertedValue<unsigned int, std::string>((*it2).second.first.size());
142                else
143                    output += "-";
144            }
145            output += "\t\t";
146            if (TclThreadManager::getInstance().getState((*it).second) == TclThread::Ready)
147                output += "ready";
148            else
149                output += "busy";
150
151            COUT(0) << output << std::endl;
152        }
153    }
154
155    void TclThreadManager::dump(unsigned int threadID)
156    {
157        if (threadID == 0)
158        {
159            // mutex orxqueue
160            boost::mutex::scoped_lock lock(TclThreadManager::getInstance().orxonoxQueueMutex_);
161
162            COUT(0) << "Queue dump of Orxonox:" << std::endl;
163            for (unsigned int index = 0; index < TclThreadManager::getInstance().orxonoxQueue_.size(); index++)
164            {
165                std::string command = TclThreadManager::getInstance().orxonoxQueue_.front();
166                COUT(0) << index << ": " << command << std::endl;
167                TclThreadManager::getInstance().orxonoxQueue_.pop();
168                TclThreadManager::getInstance().orxonoxQueue_.push(command);
169            }
170        }
171        else
172        {
173            // mutex threadqueues
174            boost::mutex::scoped_lock lock(TclThreadManager::getInstance().threadQueuesMutex_);
175
176            std::map<unsigned int, std::pair<std::queue<std::string>, boost::condition*> >::iterator it = TclThreadManager::getInstance().threadQueues_.find(threadID);
177            if (it != TclThreadManager::getInstance().threadQueues_.end())
178            {
179                COUT(0) << "Queue dump of Tcl-thread " << threadID << ":" << std::endl;
180                for (unsigned int index = 0; index < (*it).second.first.size(); index++)
181                {
182                    std::string command = (*it).second.first.front();
183                    COUT(0) << index << ": " << command << std::endl;
184                    (*it).second.first.pop();
185                    (*it).second.first.push(command);
186                }
187            }
188            else
189            {
190                COUT(1) << "Error: (" << __LINE__ << ") No Tcl-thread with ID " << threadID << "." << std::endl;
191            }
192        }
193    }
194
195    void TclThreadManager::tcl_execute(Tcl::object const &args)
196    {
197std::cout << "Tcl-thread_execute: args: " << args.get() << std::endl;
198        std::string command = args.get();
199
200        if (command.size() >= 2 && command[0] == '{' && command[command.size() - 1] == '}')
201            command = command.substr(1, command.size() - 2);
202
203        TclThreadManager::getInstance().pushCommandBack(command);
204    }
205
206    std::string TclThreadManager::tcl_query(int id, Tcl::object const &args)
207    {
208std::cout << "Tcl-thread_query: id: " << id << " args: " << args.get()  << std::endl;
209        {
210            // mutex query
211            boost::mutex::scoped_lock lock(TclThreadManager::getInstance().orxonoxQueryMutex_);
212            if (TclThreadManager::getInstance().isQuerying_ && (id == (int)TclThreadManager::getInstance().queryID_))
213            {
214                COUT(1) << "Error: Orxonox Tcl-interpreter can't be queried right now." << std::endl;
215                return "";
216            }
217        }
218
219        std::string command = args.get();
220
221        while (command.size() >= 2 && command[0] == '{' && command[command.size() - 1] == '}')
222            command = command.substr(1, command.size() - 2);
223
224        return TclThreadManager::getInstance().eval(command);
225    }
226
227    Tcl::interpreter* TclThreadManager::createTclInterpreter(unsigned int threadID) const
228    {
229        Tcl::interpreter* i = 0;
230        i = new Tcl::interpreter(TclBind::getInstance().getTclLibPath());
231
232        i->def("query", TclThreadManager::tcl_query, Tcl::variadic());
233        i->def("execute", TclThreadManager::tcl_execute, Tcl::variadic());
234
235        std::string id = getConvertedValue<unsigned int, std::string>(threadID);
236        try
237        {
238            i->eval("rename exit tclexit; proc exit {} { orxonox TclThreadManager destroy " + id + " }");
239            i->eval("proc orxonox args { query " + id + " $args }");
240            i->eval("set threadid " + id);
241            i->eval("redef_puts");
242        }
243        catch (Tcl::tcl_error const &e)
244        {
245            COUT(1) << "Tcl error: " << e.what() << std::endl;
246        }
247        catch (std::exception const &e)
248        {
249            COUT(1) << "Error while creating Tcl-thread (" << id << "): " << e.what() << std::endl;
250        }
251
252        return i;
253    }
254
255    bool TclThreadManager::createTclThread()
256    {
257        this->IDcount_++;
258        TclThread* newthread = new TclThread;
259        newthread->threadID_ = this->IDcount_;
260        newthread->interpreter_ = this->createTclInterpreter(this->IDcount_);
261        newthread->evalMutex_ = new boost::mutex;
262        newthread->stateMutex_ = new boost::mutex;
263        newthread->state_ = new TclThread::State(TclThread::Ready);
264        newthread->thread_ = new boost::thread(boost::bind(&tclThreadLoop, newthread));
265        {
266            // mutex threads
267            boost::mutex::scoped_lock lock(this->threadsMutex_);
268            this->threads_[this->IDcount_] = newthread;
269        }
270        {
271            // mutes threadqueues
272            boost::mutex::scoped_lock lock(this->threadQueuesMutex_);
273            this->threadQueues_[this->IDcount_] = std::pair<std::queue<std::string>, boost::condition*>(std::queue<std::string>(), new boost::condition());
274        }
275        return true;
276    }
277
278    bool TclThreadManager::destroyTclThread(unsigned int threadID)
279    {
280        // mutex threads
281        boost::mutex::scoped_lock lock(this->threadsMutex_);
282
283        std::map<unsigned int, TclThread*>::iterator it = this->threads_.find(threadID);
284        if (it != this->threads_.end())
285        {
286            this->setState((*it).second, TclThread::Finished);
287            (*it).second->thread_->timed_join(boost::posix_time::time_duration(0, 0, 0, 10));
288
289            delete (*it).second->interpreter_;
290            delete (*it).second->thread_;
291            delete (*it).second->evalMutex_;
292            delete (*it).second->stateMutex_;
293            delete (*it).second->state_;
294            delete (*it).second;
295
296            this->threads_.erase(it);
297            {
298                // mutex threadqueues
299                boost::mutex::scoped_lock lock(this->threadQueuesMutex_);
300                std::map<unsigned int, std::pair<std::queue<std::string>, boost::condition*> >::iterator it = this->threadQueues_.find(threadID);
301                if (it != this->threadQueues_.end())
302                {
303                    delete (*it).second.second;
304                    this->threadQueues_.erase(threadID);
305                }
306                else
307                {
308                    return false;
309                }
310            }
311            return true;
312        }
313        else
314        {
315            COUT(1) << "Error: (" << __LINE__ << ") No Tcl-thread with ID " << threadID << "." << std::endl;
316            return false;
317        }
318    }
319
320    void TclThreadManager::setState(TclThread* tclThread, TclThread::State state)
321    {
322        // mutex state
323        boost::mutex::scoped_lock lock(*tclThread->stateMutex_);
324        *tclThread->state_ = state;
325    }
326
327    TclThread::State TclThreadManager::getState(TclThread* tclThread)
328    {
329        // mutex state
330        boost::mutex::scoped_lock lock(*tclThread->stateMutex_);
331        TclThread::State state = *tclThread->state_;
332        return state;
333    }
334
335    void TclThreadManager::pushCommandBack(const std::string& command)
336    {
337        // mutex orxqueue
338        boost::mutex::scoped_lock lock(this->orxonoxQueueMutex_);
339
340        while (this->orxonoxQueue_.size() >= TCLTHREAD_MAX_QUEUE_LENGTH)
341            this->orxonoxQueueCondition_.wait(lock);
342
343        this->orxonoxQueue_.push(command);
344    }
345
346    std::string TclThreadManager::popCommandFront()
347    {
348        // mutex orxqueue
349        boost::mutex::scoped_lock lock(this->orxonoxQueueMutex_);
350
351        std::string command = this->orxonoxQueue_.front();
352        this->orxonoxQueue_.pop();
353
354        this->orxonoxQueueCondition_.notify_one();
355
356        return command;
357    }
358
359    bool TclThreadManager::isEmpty()
360    {
361        // mutex orxqueue
362        boost::mutex::scoped_lock lock(this->orxonoxQueueMutex_);
363
364        return this->orxonoxQueue_.empty();
365    }
366
367    void TclThreadManager::pushCommandBack(unsigned int threadID, const std::string& command)
368    {
369        // mutex threadqueues
370        boost::mutex::scoped_lock lock(this->threadQueuesMutex_);
371
372        std::map<unsigned int, std::pair<std::queue<std::string>, boost::condition*> >::iterator it = this->threadQueues_.find(threadID);
373        if (it != this->threadQueues_.end())
374        {
375            while ((*it).second.first.size() >= TCLTHREAD_MAX_QUEUE_LENGTH)
376                (*it).second.second->wait(lock);
377
378            (*it).second.first.push(command);
379        }
380        else
381        {
382            COUT(1) << "Error: (" << __LINE__ << ") No Tcl-thread with ID " << threadID << "." << std::endl;
383        }
384    }
385
386    std::string TclThreadManager::popCommandFront(unsigned int threadID)
387    {
388        // mutex threadqueues
389        boost::mutex::scoped_lock lock(this->threadQueuesMutex_);
390
391        std::map<unsigned int, std::pair<std::queue<std::string>, boost::condition*> >::iterator it = this->threadQueues_.find(threadID);
392        if (it != this->threadQueues_.end())
393        {
394            std::string command = (*it).second.first.front();
395            (*it).second.first.pop();
396            return command;
397        }
398        else
399        {
400            COUT(1) << "Error: (" << __LINE__ << ") No Tcl-thread with ID " << threadID << "." << std::endl;
401            return "";
402        }
403    }
404
405    bool TclThreadManager::isEmpty(unsigned int threadID)
406    {
407        // mutex threadqueues
408        boost::mutex::scoped_lock lock(this->threadQueuesMutex_);
409
410        std::map<unsigned int, std::pair<std::queue<std::string>, boost::condition*> >::const_iterator it = this->threadQueues_.find(threadID);
411        if (it != this->threadQueues_.end())
412        {
413            return (*it).second.first.empty();
414        }
415        else
416        {
417            COUT(1) << "Error: (" << __LINE__ << ") No Tcl-thread with ID " << threadID << "." << std::endl;
418            return true;
419        }
420    }
421
422    std::string TclThreadManager::eval(const std::string& command)
423    {
424        // mutex orxstate
425        boost::mutex::scoped_lock lock(this->orxonoxStateMutex_);
426
427        while (!this->isReady_)
428            this->orxonoxEvalCondition_.wait(lock);
429
430        CommandExecutor::execute(command, false);
431
432        if (CommandExecutor::getLastEvaluation().hasReturnvalue())
433            return CommandExecutor::getLastEvaluation().getReturnvalue().toString();
434
435        return "";
436    }
437
438    std::string TclThreadManager::eval(unsigned int threadID, const std::string& command)
439    {
440        {
441            // mutex query
442            boost::mutex::scoped_lock lock(this->orxonoxQueryMutex_);
443            this->isQuerying_ = true;
444            this->queryID_ = threadID;
445std::cout << "2_0\n";
446        }
447
448        {
449            // mutex threads
450            boost::mutex::scoped_lock lock(this->threadsMutex_);
451
452            std::map<unsigned int, TclThread*>::iterator it = this->threads_.find(threadID);
453            if (it != this->threads_.end())
454            {
455std::cout << "2_1\n";
456                if (this->getState((*it).second) == TclThread::Ready)
457                {
458std::cout << "2_2\n";
459                    // mutex threadeval
460                    boost::mutex::scoped_lock lock(*(*it).second->evalMutex_);
461                    try
462                    {
463std::cout << "2_3\n";
464                        return (*it).second->interpreter_->eval(command);
465                    }
466                    catch (Tcl::tcl_error const &e)
467                    {
468                        COUT(1) << "Tcl error: " << e.what() << std::endl;
469                    }
470                    catch (std::exception const &e)
471                    {
472                        COUT(1) << "Error while executing Tcl: " << e.what() << std::endl;
473                    }
474    std::cout << "2_4\n";
475                }
476                else
477                {
478                    COUT(1) << "Error: Tcl-thread with ID " << threadID << " is not ready. Wait until it's finished or start a new thread." << std::endl;
479                }
480            }
481            else
482            {
483                COUT(1) << "Error: (" << __LINE__ << ") No Tcl-thread with ID " << threadID << "." << std::endl;
484            }
485        }
486
487        {
488            // mutex query
489            boost::mutex::scoped_lock lock(this->orxonoxQueryMutex_);
490            this->isQuerying_ = false;
491            this->queryID_ = 0;
492std::cout << "2_5\n";
493        }
494
495        return "";
496    }
497
498    void TclThreadManager::tick(float dt)
499    {
500        {
501            // mutex orxstate
502            boost::mutex::scoped_lock lock(this->orxonoxStateMutex_);
503            this->isReady_ = true;
504        }
505
506        this->orxonoxEvalCondition_.notify_one();
507        boost::this_thread::yield();
508
509        {
510            // mutex orxstate
511            boost::mutex::scoped_lock lock(this->orxonoxStateMutex_);
512            this->isReady_ = false;
513        }
514
515        unsigned long maxtime = (unsigned long)(dt * 1000000 * TCLTHREAD_MAX_CPU_USAGE);
516        Ogre::Timer timer;
517        while (!TclThreadManager::getInstance().isEmpty())
518        {
519            CommandExecutor::execute(TclThreadManager::getInstance().popCommandFront(), false);
520            if (timer.getMicroseconds() > maxtime)
521                break;
522        }
523    }
524
525    void tclThreadLoop(TclThread* tclThread)
526    {
527        while (true)
528        {
529            if (!TclThreadManager::getInstance().isEmpty(tclThread->threadID_))
530            {
531                std::cout << "a\n";
532                TclThreadManager::getInstance().setState(tclThread, TclThread::Busy);
533//                if (!TclThreadManager::getInstance().isEmpty(tclThread->threadID_))
534                {
535                    try
536                    {
537                        std::cout << "c\n";
538                        throw std::exception();
539                        std::cout << "d\n";
540                    }
541                    catch (...)
542                    {
543                        std::cout << "e\n";
544                    }
545                }
546                TclThreadManager::getInstance().setState(tclThread, TclThread::Ready);
547                std::cout << "f\n";
548
549//                boost::this_thread::yield();
550//                if (TclThreadManager::getInstance().getState(tclThread) == TclThread::Finished)
551//                    return;
552            }
553        }
554
555/*        while (true)
556        {
557            TclThreadManager::getInstance().setState(tclThread, TclThread::Busy);
558            while (!TclThreadManager::getInstance().isEmpty(tclThread->threadID_))
559            {
560                try
561                {
562std::cout << "1_1\n";
563                    // mutex threadeval
564                    boost::mutex::scoped_lock lock(*tclThread->evalMutex_);
565                    tclThread->interpreter_->eval(TclThreadManager::getInstance().popCommandFront(tclThread->threadID_));
566std::cout << "1_2\n";
567                }
568                catch (Tcl::tcl_error const &e)
569                {
570                    TclThreadManager::getInstance().pushCommandBack("error Tcl error (thread " + getConvertedValue<unsigned int, std::string>(tclThread->threadID_) + "): " + e.what());
571                }
572                catch (std::exception const &e)
573                {
574                    TclThreadManager::getInstance().pushCommandBack("error Error while executing Tcl (thread " + getConvertedValue<unsigned int, std::string>(tclThread->threadID_) + "): " + e.what());
575                }
576std::cout << "1_4\n";
577            }
578            TclThreadManager::getInstance().setState(tclThread, TclThread::Ready);
579
580            while (TclThreadManager::getInstance().isEmpty(tclThread->threadID_))
581            {
582                boost::this_thread::yield();
583                if (TclThreadManager::getInstance().getState(tclThread) == TclThread::Finished)
584                    return;
585            }
586        }*/
587    }
588}
Note: See TracBrowser for help on using the repository browser.