Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

added a tcl-thread, but there are still some bugs and problems

File size: 13.7 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
56    TclThreadManager* instance = &TclThreadManager::getInstance();
57
58    TclThreadManager::TclThreadManager()
59    {
60        RegisterRootObject(TclThreadManager);
61
62        this->IDcount_ = 0;
63        this->isReady_ = false;
64    }
65
66    TclThreadManager& TclThreadManager::getInstance()
67    {
68        static TclThreadManager instance;
69        return instance;
70    }
71
72    void TclThreadManager::tclthread(unsigned int threadID, const std::string& command)
73    {
74        TclThreadManager::execute(threadID, command);
75    }
76
77    unsigned int TclThreadManager::create()
78    {
79        if (TclThreadManager::getInstance().createTclThread())
80        {
81            COUT(0) << "Created new Tcl-thread with ID " << TclThreadManager::getInstance().IDcount_ << "." << std::endl;
82            return TclThreadManager::getInstance().IDcount_;
83        }
84        return 0;
85    }
86
87    void TclThreadManager::destroy(unsigned int threadID)
88    {
89        if (TclThreadManager::getInstance().destroyTclThread(threadID))
90        {
91            COUT(0) << "Destroyed Tcl-thread with ID " << threadID << "." << std::endl;
92        }
93    }
94
95    void TclThreadManager::execute(unsigned int threadID, const std::string& command)
96    {
97        TclThreadManager::getInstance().pushCommandBack(threadID, command);
98    }
99
100    std::string TclThreadManager::query(unsigned int threadID, const std::string& command)
101    {
102        return TclThreadManager::getInstance().eval(threadID, command);
103    }
104
105    void TclThreadManager::tcl_execute(Tcl::object const &args)
106    {
107std::cout << "3_1\n";
108std::cout << "Tcl-thread_execute: args: " << args.get() << std::endl;
109        std::string command = args.get();
110
111        if (command.size() >= 2 && command[0] == '{' && command[command.size() - 1] == '}')
112            command = command.substr(1, command.size() - 2);
113
114        TclThreadManager::getInstance().pushCommandBack(command);
115    }
116
117    std::string TclThreadManager::tcl_orxonox(Tcl::object const &args)
118    {
119std::cout << "4_1\n";
120std::cout << "Tcl-thread_orxonox: args: " << args.get() << std::endl;
121        std::string command = args.get();
122
123        if (command.size() >= 2 && command[0] == '{' && command[command.size() - 1] == '}')
124            command = command.substr(1, command.size() - 2);
125
126        return TclThreadManager::getInstance().eval(command);
127    }
128
129    Tcl::interpreter* TclThreadManager::createTclInterpreter(unsigned int threadID) const
130    {
131        Tcl::interpreter* i = new Tcl::interpreter(TclBind::getInstance().getTclLibPath());
132
133        i->def("orxonox", TclThreadManager::tcl_orxonox, Tcl::variadic());
134        i->def("execute", TclThreadManager::tcl_execute, Tcl::variadic());
135
136        std::string id = getConvertedValue<unsigned int, std::string>(threadID);
137        i->eval("rename exit tclexit; proc exit {} { orxonox TclThreadManager destroy " + id + " }");
138        i->eval("set threadid " + id);
139        i->eval("redef_puts");
140
141        return i;
142    }
143
144    bool TclThreadManager::createTclThread()
145    {
146        this->IDcount_++;
147        Tcl::interpreter* i = this->createTclInterpreter(this->IDcount_);
148        boost::try_mutex* m = new boost::try_mutex;
149        boost::thread*    t = new boost::thread(boost::bind(&tclThreadLoop, i, m, this->IDcount_));
150        TclThread newthread = {i, t, m};
151        {
152            // mutex threads
153            boost::mutex::scoped_lock lock(this->threadsMutex_);
154            this->threads_[this->IDcount_] = newthread;
155        }
156        {
157            // mutes threadqueues
158            boost::mutex::scoped_lock lock(this->threadQueuesMutex_);
159            this->threadQueues_[this->IDcount_];
160        }
161        return true;
162    }
163
164    bool TclThreadManager::destroyTclThread(unsigned int threadID)
165    {
166        // mutex threads
167        boost::mutex::scoped_lock lock(this->threadsMutex_);
168
169        std::map<unsigned int, TclThread>::iterator it = this->threads_.find(threadID);
170        if (it != this->threads_.end())
171        {
172            delete (*it).second.interpreter_;
173            delete (*it).second.thread_;
174            delete (*it).second.mutex_;
175
176            this->threads_.erase(it);
177            {
178                // mutex threadqueues
179                boost::mutex::scoped_lock lock(this->threadQueuesMutex_);
180                this->threadQueues_.erase(threadID);
181            }
182            return true;
183        }
184        else
185        {
186            COUT(1) << "Error: No Tcl-thread with ID " << threadID << "." << std::endl;
187            return false;
188        }
189    }
190
191    void TclThreadManager::pushCommandBack(const std::string& command)
192    {
193        // mutex orxqueue
194        boost::mutex::scoped_lock lock(this->orxonoxQueueMutex_);
195
196        while (this->orxonoxQueue_.size() == TCLTHREAD_MAX_QUEUE_LENGTH)
197            this->orxonoxQueueCondition_.wait(lock);
198
199        this->orxonoxQueue_.push(command);
200    }
201
202    std::string TclThreadManager::popCommandFront()
203    {
204        // mutex orxqueue
205        boost::mutex::scoped_lock lock(this->orxonoxQueueMutex_);
206
207        std::string command = this->orxonoxQueue_.front();
208        this->orxonoxQueue_.pop();
209
210        this->orxonoxQueueCondition_.notify_one();
211
212        return command;
213    }
214
215    bool TclThreadManager::isEmpty()
216    {
217        // mutex orxqueue
218        boost::mutex::scoped_lock lock(this->orxonoxQueueMutex_);
219
220        return this->orxonoxQueue_.empty();
221    }
222
223    void TclThreadManager::pushCommandBack(unsigned int threadID, const std::string& command)
224    {
225        // mutex threadqueues
226        boost::mutex::scoped_lock lock(this->threadQueuesMutex_);
227
228        std::map<unsigned int, std::queue<std::string> >::iterator it = this->threadQueues_.find(threadID);
229        if (it != this->threadQueues_.end())
230        {
231            (*it).second.push(command);
232        }
233        else
234        {
235            COUT(1) << "Error: No Tcl-thread with ID " << threadID << "." << std::endl;
236        }
237    }
238
239    std::string TclThreadManager::popCommandFront(unsigned int threadID)
240    {
241        // mutex threadqueues
242        boost::mutex::scoped_lock lock(this->threadQueuesMutex_);
243
244        std::map<unsigned int, std::queue<std::string> >::iterator it = this->threadQueues_.find(threadID);
245        if (it != this->threadQueues_.end())
246        {
247            std::string command = (*it).second.front();
248            (*it).second.pop();
249            return command;
250        }
251        else
252        {
253            COUT(1) << "Error: No Tcl-thread with ID " << threadID << "." << std::endl;
254            return "";
255        }
256    }
257
258    bool TclThreadManager::isEmpty(unsigned int threadID)
259    {
260        // mutex threadqueues
261        boost::mutex::scoped_lock lock(this->threadQueuesMutex_);
262
263        std::map<unsigned int, std::queue<std::string> >::iterator it = this->threadQueues_.find(threadID);
264        if (it != this->threadQueues_.end())
265        {
266            return (*it).second.empty();
267        }
268        else
269        {
270            COUT(1) << "Error: No Tcl-thread with ID " << threadID << "." << std::endl;
271            return true;
272        }
273    }
274
275    std::string TclThreadManager::eval(const std::string& command)
276    {
277        boost::mutex::scoped_lock lock(this->orxonoxEvalMutex_);
278
279        while (!this->isReady_)
280            this->orxonoxEvalCondition_.wait(lock);
281
282        CommandExecutor::execute(command, false);
283
284        if (CommandExecutor::getLastEvaluation().hasReturnvalue())
285            return CommandExecutor::getLastEvaluation().getReturnvalue().toString();
286
287        return "";
288    }
289
290    std::string TclThreadManager::eval(unsigned int threadID, const std::string& command)
291    {
292        // mutex threads
293        boost::mutex::scoped_lock lock(this->threadsMutex_);
294
295        std::map<unsigned int, TclThread>::iterator it = this->threads_.find(threadID);
296        if (it != this->threads_.end())
297        {
298            boost::try_mutex::scoped_try_lock lock(*(*it).second.mutex_, boost::defer_lock_t());
299            bool threadIsReady = false;
300            try
301            {
302                threadIsReady = lock.try_lock();
303            }
304            catch(boost::lock_error& e){ std::cout << "lockerror thread" << std::endl; }
305
306            if (threadIsReady)
307            {
308                try
309                {
310std::cout << "2_1\n";
311                    return (*it).second.interpreter_->eval(command);
312                }
313                catch (Tcl::tcl_error const &e)
314                {
315std::cout << "2_2\n";
316                    COUT(1) << "Tcl error: " << e.what() << std::endl;
317                }
318                catch (std::exception const &e)
319                {
320std::cout << "2_3\n";
321                    COUT(1) << "Error while executing tcl: " << e.what() << std::endl;
322                }
323            }
324            else
325            {
326                COUT(1) << "Error: Tcl-thread with ID " << threadID << " is not ready. Wait until it's finished or start a new thread." << std::endl;
327            }
328        }
329        else
330        {
331            COUT(1) << "Error: No Tcl-thread with ID " << threadID << "." << std::endl;
332        }
333        return "";
334    }
335
336    void TclThreadManager::tick(float dt)
337    {
338        {
339            boost::mutex::scoped_lock lock(this->orxonoxEvalMutex_);
340            this->isReady_ = true;
341        }
342
343        this->orxonoxEvalCondition_.notify_one();
344        boost::this_thread::yield();
345
346        {
347            boost::mutex::scoped_lock lock(this->orxonoxEvalMutex_);
348            this->isReady_ = false;
349        }
350
351        unsigned long maxtime = (unsigned long)(dt * 1000000 * TCLTHREAD_MAX_CPU_USAGE);
352        Ogre::Timer timer;
353        while (!TclThreadManager::getInstance().isEmpty())
354        {
355            CommandExecutor::execute(TclThreadManager::getInstance().popCommandFront(), false);
356            if (timer.getMicroseconds() > maxtime)
357                break;
358        }
359    }
360
361    void tclThreadLoop(Tcl::interpreter* interpreter, boost::try_mutex* mutex, unsigned int threadID)
362    {
363        while (true)
364        {
365            {
366                boost::try_mutex::scoped_try_lock lock(*mutex, boost::defer_lock_t());
367                bool threadIsReady = false;
368                try
369                {
370                    threadIsReady = lock.try_lock();
371                }
372                catch(boost::lock_error& e){ std::cout << "lockerror thread" << std::endl; }
373
374                if (threadIsReady)
375                {
376                    while (!TclThreadManager::getInstance().isEmpty(threadID))
377                    {
378                        try
379                        {
380std::cout << "1\n";
381std::cout << threadID << std::endl;
382std::string temp = TclThreadManager::getInstance().popCommandFront(threadID);
383std::cout << temp << std::endl;
384                            interpreter->eval(temp);
385std::cout << "2\n";
386                        }
387                        catch (Tcl::tcl_error const &e)
388                        {
389std::cout << "3\n";
390                            TclThreadManager::getInstance().pushCommandBack("error Tcl error (thread " + getConvertedValue<unsigned int, std::string>(threadID) + "): " + e.what());
391                        }
392                        catch (std::exception const &e)
393                        {
394std::cout << "4\n";
395                            TclThreadManager::getInstance().pushCommandBack("error Error while executing tcl (thread " + getConvertedValue<unsigned int, std::string>(threadID) + "): " + e.what());
396                        }
397                        catch (...)
398                        {
399std::cout << "5\n";
400                            TclThreadManager::getInstance().pushCommandBack("error Error while executing tcl (thread " + getConvertedValue<unsigned int, std::string>(threadID) + ").");
401                        }
402                    }
403                }
404            }
405
406            while (TclThreadManager::getInstance().isEmpty(threadID))
407                boost::this_thread::yield();
408        }
409    }
410}
Note: See TracBrowser for help on using the repository browser.