Changeset 3313
- Timestamp:
- Jul 19, 2009, 2:58:24 PM (15 years ago)
- Location:
- code/trunk/src/core
- Files:
-
- 1 deleted
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
code/trunk/src/core/CorePrereqs.h
r3308 r3313 150 150 class SubclassIdentifier; 151 151 class TclBind; 152 struct TclInterpreterBundle;153 template <class T>154 class TclThreadList;155 152 class TclThreadManager; 156 153 class Template; … … 204 201 // Boost 205 202 namespace boost 206 { 203 { 207 204 namespace filesystem 208 205 { … … 213 210 class thread; 214 211 class mutex; 215 class shared_mutex;216 class condition_variable;217 212 } 218 213 -
code/trunk/src/core/Game.cc
r3307 r3313 246 246 247 247 // UPDATE, Core first 248 bool threwException = false;249 248 try 250 249 { 251 250 this->core_->update(*this->gameClock_); 252 251 } 253 catch (const std::exception& ex)254 {255 threwException = true;256 COUT(0) << "Exception while ticking the Core: " << ex.what() << std::endl;257 }258 252 catch (...) 259 {260 threwException = true;261 }262 if (threwException)263 253 { 264 254 COUT(0) << "An exception occured while ticking the Core. This should really never happen!" << std::endl; -
code/trunk/src/core/IRC.cc
r3307 r3313 53 53 { 54 54 unsigned int threadID = IRC_TCL_THREADID; 55 this->interpreter_ = TclThreadManager::createWithId(threadID); 55 TclThreadManager::createID(threadID); 56 this->interpreter_ = TclThreadManager::getInstance().getTclInterpreter(threadID); 56 57 57 58 try -
code/trunk/src/core/TclBind.cc
r3307 r3313 79 79 this->interpreter_->def("orxonox::crossquery", TclThreadManager::tcl_crossquery, Tcl::variadic()); 80 80 this->interpreter_->def("execute", TclBind::tcl_execute, Tcl::variadic()); 81 this->interpreter_->def("orxonox::crossexecute", TclThreadManager::tcl_crossexecute, Tcl::variadic());82 81 83 82 try 84 83 { 85 this->interpreter_->eval("proc query args { orxonox::query [join $args] }"); 86 this->interpreter_->eval("proc crossquery {id args} { orxonox::crossquery 0 $id [join $args] }"); 87 this->interpreter_->eval("proc crossexecute {id args} { orxonox::crossquery 0 $id [join $args] }"); 84 this->interpreter_->eval("proc query args { orxonox::query $args }"); 85 this->interpreter_->eval("proc crossquery {id args} { orxonox::crossquery 0 $id $args }"); 88 86 this->interpreter_->eval("set id 0"); 89 87 this->interpreter_->eval("rename exit tcl::exit; proc exit {} { execute exit }"); -
code/trunk/src/core/TclThreadManager.cc
r3307 r3313 30 30 31 31 #include <boost/bind.hpp> 32 #include <boost/thread/condition.hpp> 33 #include <boost/thread/mutex.hpp> 32 34 #include <boost/thread/thread.hpp> 33 #include <boost/thread/locks.hpp>34 #include <boost/thread/shared_mutex.hpp>35 35 #include <OgreTimer.h> 36 36 #include <cpptcl/cpptcl.h> 37 37 38 38 #include "util/Convert.h" 39 #include "util/Debug.h" 39 40 #include "Clock.h" 40 41 #include "CommandExecutor.h" … … 42 43 #include "CoreIncludes.h" 43 44 #include "TclBind.h" 44 #include "TclThreadList.h"45 45 46 46 namespace orxonox 47 47 { 48 const unsigned int TCLTHREADMANAGER_MAX_QUEUE_LENGTH = 100; 48 49 const float TCLTHREADMANAGER_MAX_CPU_USAGE = 0.50f; 49 50 … … 54 55 SetConsoleCommand(TclThreadManager, execute, false).argumentCompleter(0, autocompletion::tclthreads()); 55 56 SetConsoleCommand(TclThreadManager, query, false).argumentCompleter(0, autocompletion::tclthreads()); 56 57 /**58 @brief A struct containing all informations about a Tcl-interpreter59 */ 57 SetConsoleCommand(TclThreadManager, status, false); 58 SetConsoleCommand(TclThreadManager, dump, false).argumentCompleter(0, autocompletion::tclthreads()); 59 SetConsoleCommand(TclThreadManager, flush, false).argumentCompleter(0, autocompletion::tclthreads()); 60 60 61 struct TclInterpreterBundle 61 62 { 62 TclInterpreterBundle()63 { 64 this->lock_ = new boost::unique_lock<boost::mutex>(this->mutex_, boost::defer_lock);65 this->bRunning_ = true;66 } 67 68 ~TclInterpreterBundle()69 {70 delete this->lock_; 71 }72 73 unsigned int id_; ///< The id of the interpreter 74 Tcl::interpreter* interpreter_; ///< The Tcl-interpreter75 boost::mutex mutex_; ///< A mutex to lock the interpreter while it's being used76 boost::unique_lock<boost::mutex>* lock_; ///< The corresponding lock for the mutex 77 TclThreadList<std::string> queue_; ///< The command queue for commands passed by execute(command)78 TclThreadList<unsigned int> queriers_; ///< A list containing the id's of all other threads sending a query to this interpreter (to avoid circular queries and deadlocks)79 boo l bRunning_; ///< This variable stays true until destroy() gets called63 unsigned int id_; 64 65 std::list<std::string> queue_; 66 boost::mutex queueMutex_; 67 68 Tcl::interpreter* interpreter_; 69 std::string interpreterName_; 70 boost::try_mutex interpreterMutex_; 71 72 std::list<unsigned int> queriers_; 73 boost::mutex queriersMutex_; 74 75 bool running_; 76 boost::mutex runningMutex_; 77 78 bool finished_; 79 boost::mutex finishedMutex_; 80 boost::condition finishedCondition_; 80 81 }; 81 82 82 TclThreadManager* TclThreadManager::singletonPtr_s = 0; 83 84 /** 85 @brief Constructor 86 @param interpreter A pointer to the standard Tcl-interpreter (see @ref TclBind) 87 */ 83 84 static boost::thread::id threadID_g; 85 static boost::mutex bundlesMutex_g; 86 static boost::condition fullQueueCondition_g; 87 static boost::condition orxonoxEvalCondition_g; 88 89 TclThreadManager* TclThreadManager::singletonRef_s = 0; 90 88 91 TclThreadManager::TclThreadManager(Tcl::interpreter* interpreter) 92 : orxonoxInterpreterBundle_(new TclInterpreterBundle()) 89 93 { 90 94 RegisterRootObject(TclThreadManager); 91 95 92 assert(TclThreadManager::singletonPtr_s == 0); 93 TclThreadManager::singletonPtr_s = this; 94 95 this->numInterpreterBundles_ = 0; 96 97 this->interpreterBundlesMutex_ = new boost::shared_mutex(); 98 this->mainInterpreterMutex_ = new boost::mutex(); 99 this->messageQueue_ = new TclThreadList<std::string>(); 100 101 TclInterpreterBundle* newbundle = new TclInterpreterBundle(); 102 newbundle->id_ = 0; 103 newbundle->interpreter_ = interpreter; 104 newbundle->lock_->lock(); 105 106 { 107 boost::unique_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_); 108 this->interpreterBundles_[0] = newbundle; 109 } 110 } 111 112 /** 113 @brief Destructor 114 */ 96 assert(singletonRef_s == 0); 97 singletonRef_s = this; 98 99 this->threadCounter_ = 0; 100 this->orxonoxInterpreterBundle_->id_ = 0; 101 this->orxonoxInterpreterBundle_->interpreter_ = interpreter; 102 threadID_g = boost::this_thread::get_id(); 103 } 104 115 105 TclThreadManager::~TclThreadManager() 116 106 { 117 TclThreadManager::singletonPtr_s = 0; 118 119 delete this->interpreterBundlesMutex_; 120 delete this->mainInterpreterMutex_; 121 delete this->messageQueue_; 122 } 123 124 /** 125 @brief The "main loop" of the TclThreadManager. Creates new threads if needed and handles queries and queued commands for the main interpreter. 126 */ 127 void TclThreadManager::update(const Clock& time) 128 { 129 // Get the bundle of the main interpreter (0) 130 TclInterpreterBundle* bundle = this->getInterpreterBundle(0); 107 unsigned int threadID; 108 { 109 boost::mutex::scoped_lock bundles_lock(bundlesMutex_g); 110 if (this->interpreterBundles_.begin() == this->interpreterBundles_.end()) 111 return; 112 else 113 threadID = this->interpreterBundles_.begin()->first; 114 } 115 this->destroy(threadID); 116 117 delete this->orxonoxInterpreterBundle_; 118 119 singletonRef_s = 0; 120 } 121 122 unsigned int TclThreadManager::create() 123 { 124 boost::mutex::scoped_lock bundles_lock(bundlesMutex_g); 125 TclThreadManager::getInstance().threadCounter_++; 126 std::string name = multi_cast<std::string>(TclThreadManager::getInstance().threadCounter_); 127 128 TclInterpreterBundle* bundle = new TclInterpreterBundle; 129 bundle->id_ = TclThreadManager::getInstance().threadCounter_; 130 bundle->interpreter_ = TclThreadManager::getInstance().createNewTclInterpreter(name); 131 bundle->interpreterName_ = name; 132 bundle->running_ = true; 133 bundle->finished_ = true; 134 135 TclThreadManager::getInstance().interpreterBundles_[TclThreadManager::getInstance().threadCounter_] = bundle; 136 COUT(0) << "Created new Tcl-interpreter with ID " << TclThreadManager::getInstance().threadCounter_ << std::endl; 137 return TclThreadManager::getInstance().threadCounter_; 138 } 139 140 unsigned int TclThreadManager::createID(unsigned int threadID) 141 { 142 unsigned int temp = TclThreadManager::getInstance().threadCounter_; 143 TclThreadManager::getInstance().threadCounter_ = threadID - 1; 144 TclThreadManager::create(); 145 TclThreadManager::getInstance().threadCounter_ = temp; 146 return threadID; 147 } 148 149 void TclThreadManager::destroy(unsigned int threadID) 150 { 151 TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(threadID); 131 152 if (bundle) 132 153 { 133 // Unlock the mutex to allow other threads accessing the main interpreter 134 bundle->lock_->unlock(); 135 136 // Lock the main interpreter mutex once to synchronize with threads that want to query the main interpreter 137 { 138 boost::unique_lock<boost::mutex> lock(*this->mainInterpreterMutex_); 139 } 140 141 // Lock the mutex again to gain exclusive access to the interpreter for the rest of the mainloop 142 bundle->lock_->lock(); 143 144 // Execute commands in the queues of the threaded interpreters 145 { 146 boost::shared_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_); 147 for (std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = this->interpreterBundles_.begin(); it != this->interpreterBundles_.end(); ++it) 154 { 155 boost::mutex::scoped_lock running_lock(bundle->runningMutex_); 156 bundle->running_ = false; 157 } 158 while (true) 159 { 148 160 { 149 if (it->first == 0) 150 continue; // We'll handle the default interpreter later (and without threads of course) 151 152 TclInterpreterBundle* bundle = it->second; 153 if (!bundle->queue_.empty()) 161 boost::mutex::scoped_lock finished_lock(bundle->finishedMutex_); 162 if (bundle->finished_) 154 163 { 155 // There are commands in the queue 164 boost::mutex::scoped_lock bundles_lock(bundlesMutex_g); 165 boost::mutex::scoped_try_lock interpreter_lock(bundle->interpreterMutex_); 156 166 try 157 167 { 158 if (!bundle->lock_->owns_lock() && bundle->lock_->try_lock())168 while (!interpreter_lock.try_lock()) 159 169 { 160 // We sucessfully obtained a lock for the interpreter 161 std::string command; 162 if (bundle->queue_.try_pop_front(&command)) 163 { 164 // Start a thread to execute the command 165 boost::thread(boost::bind(&tclThread, bundle, command)); 166 } 167 else 168 { 169 // Somehow the queue become empty (maybe multiple consumers) - unlock the mutex 170 bundle->lock_->unlock(); 171 } 170 orxonoxEvalCondition_g.notify_one(); 171 boost::this_thread::yield(); 172 172 } 173 } 174 catch (...) 175 { 176 // A lock error occurred - this is possible if the lock gets locked between !bundle->lock_->owns_lock() and bundle->lock_->try_lock() 177 // This isn't too bad, just continue 178 } 173 } catch (...) {} 174 delete bundle->interpreter_; 175 delete bundle; 176 TclThreadManager::getInstance().interpreterBundles_.erase(threadID); 177 break; 179 178 } 180 179 } 181 } 182 183 // Execute commands in the message queue 184 if (!this->messageQueue_->empty()) 185 { 186 std::string command; 187 while (true) 180 181 orxonoxEvalCondition_g.notify_one(); 182 boost::this_thread::yield(); 183 } 184 185 COUT(0) << "Destroyed Tcl-interpreter with ID " << threadID << std::endl; 186 } 187 } 188 189 void TclThreadManager::execute(unsigned int threadID, const std::string& _command) 190 { 191 std::string command = stripEnclosingBraces(_command); 192 193 if (threadID == 0) 194 TclThreadManager::getInstance().pushCommandToQueue(command); 195 else 196 TclThreadManager::getInstance().pushCommandToQueue(threadID, command); 197 } 198 199 std::string TclThreadManager::query(unsigned int threadID, const std::string& command) 200 { 201 return TclThreadManager::getInstance().evalQuery(TclThreadManager::getInstance().orxonoxInterpreterBundle_->id_, threadID, command); 202 } 203 204 void TclThreadManager::status() 205 { 206 COUT(0) << "Thread ID" << '\t' << "Queue size" << '\t' << "State" << std::endl; 207 208 std::string output = "Orxonox"; 209 output += "\t\t"; 210 { 211 boost::mutex::scoped_lock queue_lock(TclThreadManager::getInstance().orxonoxInterpreterBundle_->queueMutex_); 212 output += multi_cast<std::string>(TclThreadManager::getInstance().orxonoxInterpreterBundle_->queue_.size()); 213 } 214 output += "\t\t"; 215 output += "busy"; 216 COUT(0) << output << std::endl; 217 218 boost::mutex::scoped_lock bundles_lock(bundlesMutex_g); 219 for (std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = TclThreadManager::getInstance().interpreterBundles_.begin(); it != TclThreadManager::getInstance().interpreterBundles_.end(); ++it) 220 { 221 std::string output = multi_cast<std::string>((*it).first); 222 output += "\t\t"; 223 { 224 boost::mutex::scoped_lock queue_lock((*it).second->queueMutex_); 225 output += multi_cast<std::string>((*it).second->queue_.size()); 226 } 227 output += "\t\t"; 228 { 229 boost::mutex::scoped_try_lock interpreter_lock((*it).second->interpreterMutex_); 230 if (interpreter_lock.try_lock()) 231 output += "ready"; 232 else 233 output += "busy"; 234 } 235 COUT(0) << output << std::endl; 236 } 237 } 238 239 void TclThreadManager::dump(unsigned int threadID) 240 { 241 TclInterpreterBundle* bundle = 0; 242 if (threadID == 0) 243 { 244 bundle = TclThreadManager::getInstance().orxonoxInterpreterBundle_; 245 COUT(0) << "Queue dump of Orxonox:" << std::endl; 246 } 247 else 248 { 249 if ((bundle = TclThreadManager::getInstance().getInterpreterBundle(threadID))) 250 { 251 COUT(0) << "Queue dump of Tcl-thread " << threadID << ":" << std::endl; 252 } 253 else 254 return; 255 } 256 257 boost::mutex::scoped_lock queue_lock(bundle->queueMutex_); 258 unsigned int index = 0; 259 for (std::list<std::string>::const_iterator it = bundle->queue_.begin(); it != bundle->queue_.end(); ++it) 260 { 261 index++; 262 COUT(0) << index << ": " << (*it) << std::endl; 263 } 264 } 265 266 void TclThreadManager::flush(unsigned int threadID) 267 { 268 TclInterpreterBundle* bundle = 0; 269 if (threadID == 0) 270 bundle = TclThreadManager::getInstance().orxonoxInterpreterBundle_; 271 else 272 if (!(bundle = TclThreadManager::getInstance().getInterpreterBundle(threadID))) 273 return; 274 275 boost::mutex::scoped_lock queue_lock(bundle->queueMutex_); 276 bundle->queue_.clear(); 277 if (threadID == 0) 278 { 279 COUT(0) << "Flushed queue of Orxonox Tcl-interpreter." << std::endl; 280 } 281 else 282 { 283 COUT(0) << "Flushed queue of Tcl-interpreter " << threadID << "." << std::endl; 284 } 285 } 286 287 void TclThreadManager::tcl_execute(Tcl::object const &args) 288 { 289 TclThreadManager::getInstance().pushCommandToQueue(stripEnclosingBraces(args.get())); 290 } 291 292 std::string TclThreadManager::tcl_query(int querierID, Tcl::object const &args) 293 { 294 return TclThreadManager::getInstance().evalQuery(static_cast<unsigned int>(querierID), stripEnclosingBraces(args.get())); 295 } 296 297 std::string TclThreadManager::tcl_crossquery(int querierID, int threadID, Tcl::object const &args) 298 { 299 return TclThreadManager::getInstance().evalQuery(static_cast<unsigned int>(querierID), static_cast<unsigned int>(threadID), stripEnclosingBraces(args.get())); 300 } 301 302 bool TclThreadManager::tcl_running(int threadID) 303 { 304 TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(static_cast<unsigned int>(threadID)); 305 if (bundle) 306 { 307 boost::mutex::scoped_lock running_lock(bundle->runningMutex_); 308 return bundle->running_; 309 } 310 return false; 311 } 312 313 Tcl::interpreter* TclThreadManager::createNewTclInterpreter(const std::string& threadID) 314 { 315 Tcl::interpreter* i = 0; 316 i = new Tcl::interpreter(TclBind::getInstance().getTclLibPath()); 317 318 try 319 { 320 i->def("orxonox::query", TclThreadManager::tcl_query, Tcl::variadic()); 321 i->def("orxonox::crossquery", TclThreadManager::tcl_crossquery, Tcl::variadic()); 322 i->def("orxonox::execute", TclThreadManager::tcl_execute, Tcl::variadic()); 323 i->def("orxonox::running", TclThreadManager::tcl_running); 324 325 i->def("execute", TclThreadManager::tcl_execute, Tcl::variadic()); 326 i->eval("proc query args { orxonox::query " + threadID + " $args }"); 327 i->eval("proc crossquery {id args} { orxonox::crossquery " + threadID + " $id $args }"); 328 i->eval("set id " + threadID); 329 330 i->eval("rename exit tcl::exit"); 331 i->eval("proc exit {} { orxonox TclThreadManager destroy " + threadID + " }"); 332 333 i->eval("redef_puts"); 334 335 // i->eval("rename while tcl::while"); 336 // i->eval("proc while {test command} { tcl::while {[uplevel 1 expr $test]} {uplevel 1 $command} }"); // (\"$test\" && [orxonox::running " + threadID + "]]) 337 // i->eval("rename for tcl::for"); 338 // i->eval("proc for {start test next command} { uplevel tcl::for \"$start\" \"$test\" \"$next\" \"$command\" }"); 339 } 340 catch (Tcl::tcl_error const &e) 341 { COUT(1) << "Tcl error while creating Tcl-interpreter (" << threadID << "): " << e.what() << std::endl; } 342 catch (std::exception const &e) 343 { COUT(1) << "Error while creating Tcl-interpreter (" << threadID << "): " << e.what() << std::endl; } 344 345 return i; 346 } 347 348 TclInterpreterBundle* TclThreadManager::getInterpreterBundle(unsigned int threadID) 349 { 350 boost::mutex::scoped_lock bundles_lock(bundlesMutex_g); 351 std::map<unsigned int, TclInterpreterBundle*>::iterator it = this->interpreterBundles_.find(threadID); 352 if (it != this->interpreterBundles_.end()) 353 { 354 return (*it).second; 355 } 356 else 357 { 358 this->error("Error: No Tcl-interpreter with ID " + multi_cast<std::string>(threadID) + " existing."); 359 return 0; 360 } 361 } 362 363 Tcl::interpreter* TclThreadManager::getTclInterpreter(unsigned int threadID) 364 { 365 return this->getInterpreterBundle(threadID)->interpreter_; 366 } 367 368 std::string TclThreadManager::dumpList(const std::list<unsigned int>& list) 369 { 370 std::string output = ""; 371 for (std::list<unsigned int>::const_iterator it = list.begin(); it != list.end(); ++it) 372 { 373 if (it != list.begin()) 374 output += " "; 375 376 output += multi_cast<std::string>(*it); 377 } 378 return output; 379 } 380 381 void TclThreadManager::error(const std::string& error) 382 { 383 if (boost::this_thread::get_id() != threadID_g) 384 { 385 boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_->queueMutex_); 386 if (this->orxonoxInterpreterBundle_->queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH) 387 { 388 boost::this_thread::yield(); 389 return; 390 } 391 } 392 393 this->forceCommandToFrontOfQueue("error " + error); 394 } 395 396 void TclThreadManager::debug(const std::string& error) 397 { 398 if (boost::this_thread::get_id() != threadID_g) 399 { 400 boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_->queueMutex_); 401 if (this->orxonoxInterpreterBundle_->queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH) 402 { 403 boost::this_thread::yield(); 404 return; 405 } 406 } 407 408 this->forceCommandToFrontOfQueue("debug " + error); 409 } 410 411 void TclThreadManager::pushCommandToQueue(const std::string& command) 412 { 413 boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_->queueMutex_); 414 while (this->orxonoxInterpreterBundle_->queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH) 415 fullQueueCondition_g.wait(queue_lock); 416 417 this->orxonoxInterpreterBundle_->queue_.push_back(command); 418 } 419 420 void TclThreadManager::forceCommandToFrontOfQueue(const std::string& command) 421 { 422 boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_->queueMutex_); 423 this->orxonoxInterpreterBundle_->queue_.push_front(command); 424 } 425 426 std::string TclThreadManager::popCommandFromQueue() 427 { 428 boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_->queueMutex_); 429 std::string temp = this->orxonoxInterpreterBundle_->queue_.front(); 430 this->orxonoxInterpreterBundle_->queue_.pop_front(); 431 fullQueueCondition_g.notify_one(); 432 return temp; 433 } 434 435 bool TclThreadManager::queueIsEmpty() 436 { 437 boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_->queueMutex_); 438 return this->orxonoxInterpreterBundle_->queue_.empty(); 439 } 440 441 void TclThreadManager::pushCommandToQueue(unsigned int threadID, const std::string& command) 442 { 443 TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID); 444 if (bundle) 445 { 446 boost::mutex::scoped_lock queue_lock(bundle->queueMutex_); 447 if (bundle->queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH) 448 { 449 this->error("Error: Queue of Tcl-interpreter " + multi_cast<std::string>(threadID) + " is full, couldn't add command."); 450 return; 451 } 452 453 bundle->queue_.push_back(command); 454 } 455 } 456 457 std::string TclThreadManager::popCommandFromQueue(unsigned int threadID) 458 { 459 TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID); 460 if (bundle) 461 { 462 boost::mutex::scoped_lock queue_lock(bundle->queueMutex_); 463 std::string temp = bundle->queue_.front(); 464 bundle->queue_.pop_front(); 465 return temp; 466 } 467 return ""; 468 } 469 470 bool TclThreadManager::queueIsEmpty(unsigned int threadID) 471 { 472 TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID); 473 if (bundle) 474 { 475 boost::mutex::scoped_lock queue_lock(bundle->queueMutex_); 476 return bundle->queue_.empty(); 477 } 478 return true; 479 } 480 481 bool TclThreadManager::updateQueriersList(TclInterpreterBundle* querier, TclInterpreterBundle* target) 482 { 483 if (querier == target) 484 return false; 485 486 boost::mutex::scoped_lock queriers_lock(target->queriersMutex_); 487 488 { 489 boost::mutex::scoped_lock queriers_lock(querier->queriersMutex_); 490 target->queriers_.insert(target->queriers_.end(), querier->queriers_.begin(), querier->queriers_.end()); 491 } 492 493 target->queriers_.insert(target->queriers_.end(), querier->id_); 494 495 if (std::find(target->queriers_.begin(), target->queriers_.end(), target->id_) != target->queriers_.end()) 496 { 497 this->error("Error: Circular query (" + this->dumpList(target->queriers_) + " -> " + multi_cast<std::string>(target->id_) + "), couldn't query Tcl-interpreter with ID " + multi_cast<std::string>(target->id_) + " from other interpreter with ID " + multi_cast<std::string>(querier->id_) + "."); 498 return false; 499 } 500 501 return true; 502 } 503 504 std::string TclThreadManager::evalQuery(unsigned int querierID, const std::string& command) 505 { 506 TclInterpreterBundle* querier = this->getInterpreterBundle(querierID); 507 std::string output = ""; 508 if (querier) 509 { 510 if (this->updateQueriersList(querier, this->orxonoxInterpreterBundle_)) 511 { 512 boost::mutex::scoped_lock interpreter_lock(this->orxonoxInterpreterBundle_->interpreterMutex_); 513 orxonoxEvalCondition_g.wait(interpreter_lock); 514 515 if (!CommandExecutor::execute(command, false)) 516 this->error("Error: Can't execute command \"" + command + "\"!"); 517 518 if (CommandExecutor::getLastEvaluation().hasReturnvalue()) 519 output = CommandExecutor::getLastEvaluation().getReturnvalue().getString(); 520 } 521 522 boost::mutex::scoped_lock queriers_lock(this->orxonoxInterpreterBundle_->queriersMutex_); 523 this->orxonoxInterpreterBundle_->queriers_.clear(); 524 } 525 return output; 526 } 527 528 std::string TclThreadManager::evalQuery(unsigned int querierID, unsigned int threadID, const std::string& command) 529 { 530 TclInterpreterBundle* target = 0; 531 if (threadID) 532 target = this->getInterpreterBundle(threadID); 533 else 534 target = this->orxonoxInterpreterBundle_; 535 536 std::string output = ""; 537 if (target) 538 { 539 TclInterpreterBundle* querier = 0; 540 if (querierID) 541 querier = this->getInterpreterBundle(querierID); 542 else 543 querier = this->orxonoxInterpreterBundle_; 544 545 if (querier) 546 { 547 if (this->updateQueriersList(querier, target)) 188 548 { 189 // Pop the front value from the list (break the loop if there are no elements in the list) 190 if (!this->messageQueue_->try_pop_front(&command)) 191 break; 192 193 // Execute the command 194 CommandExecutor::execute(command, false); 195 } 196 } 197 198 // Execute commands in the queue of the main interpreter 199 if (!bundle->queue_.empty()) 200 { 201 // Calculate the time we have until we reach the maximal cpu usage 202 unsigned long maxtime = (unsigned long)(time.getDeltaTime() * 1000000 * TCLTHREADMANAGER_MAX_CPU_USAGE); 203 204 Ogre::Timer timer; 205 std::string command; 206 207 while (timer.getMicroseconds() < maxtime) 208 { 209 // Pop the front value from the list (break the loop if there are no elements in the list) 210 if (!bundle->queue_.try_pop_front(&command)) 211 break; 212 213 // Execute the command 214 CommandExecutor::execute(command, false); 215 } 216 } 217 } 218 } 219 220 /** 221 @brief Creates a new Tcl-interpreter. 222 */ 223 unsigned int TclThreadManager::create() 224 { 225 TclThreadManager::getInstance().numInterpreterBundles_++; 226 TclThreadManager::createWithId(TclThreadManager::getInstance().numInterpreterBundles_); 227 COUT(0) << "Created new Tcl-interpreter with ID " << TclThreadManager::getInstance().numInterpreterBundles_ << std::endl; 228 return TclThreadManager::getInstance().numInterpreterBundles_; 229 } 230 231 /** 232 @brief Creates a new Tcl-interpreter with a given id. 233 234 Use with caution - if the id collides with an already existing interpreter, this call will fail. 235 This will also be a problem, if the auto-numbered interpreters (by using create()) reach an id 236 which was previously used in this function. Use high numbers to be safe. 237 */ 238 Tcl::interpreter* TclThreadManager::createWithId(unsigned int id) 239 { 240 TclInterpreterBundle* newbundle = new TclInterpreterBundle(); 241 newbundle->id_ = id; 242 newbundle->interpreter_ = new Tcl::interpreter(TclBind::getInstance().getTclLibPath()); 243 244 // Initialize the new interpreter 245 try 246 { 247 std::string id_string = getConvertedValue<unsigned int, std::string>(id); 248 249 // Define the functions which are implemented in C++ 250 newbundle->interpreter_->def("orxonox::execute", TclThreadManager::tcl_execute, Tcl::variadic()); 251 newbundle->interpreter_->def("orxonox::crossexecute", TclThreadManager::tcl_crossexecute, Tcl::variadic()); 252 newbundle->interpreter_->def("orxonox::query", TclThreadManager::tcl_query, Tcl::variadic()); 253 newbundle->interpreter_->def("orxonox::crossquery", TclThreadManager::tcl_crossquery, Tcl::variadic()); 254 newbundle->interpreter_->def("orxonox::running", TclThreadManager::tcl_running); 255 256 // Create threadspecific shortcuts for the functions above 257 newbundle->interpreter_->def("execute", TclThreadManager::tcl_execute, Tcl::variadic()); 258 newbundle->interpreter_->def("crossexecute", TclThreadManager::tcl_crossexecute, Tcl::variadic()); 259 newbundle->interpreter_->eval("proc query args { orxonox::query " + id_string + " $args }"); 260 newbundle->interpreter_->eval("proc crossquery {id args} { orxonox::crossquery " + id_string + " $id $args }"); 261 262 // Define a variable containing the thread id 263 newbundle->interpreter_->eval("set id " + id_string); 264 265 // Use our own exit function to avoid shutting down the whole program instead of just the interpreter 266 newbundle->interpreter_->eval("rename exit tcl::exit"); 267 newbundle->interpreter_->eval("proc exit {} { execute TclThreadManager destroy " + id_string + " }"); 268 269 // Redefine some native functions 270 newbundle->interpreter_->eval("redef_puts"); 271 272 // newbundle->interpreter_->eval("rename while tcl::while"); 273 // newbundle->interpreter_->eval("proc while {test command} { tcl::while {[uplevel 1 expr $test]} {uplevel 1 $command} }"); // (\"$test\" && [orxonox::running " + id + "]]) 274 // newbundle->interpreter_->eval("rename for tcl::for"); 275 // newbundle->interpreter_->eval("proc for {start test next command} { uplevel tcl::for \"$start\" \"$test\" \"$next\" \"$command\" }"); 276 } 277 catch (const Tcl::tcl_error& e) 278 { newbundle->interpreter_ = 0; COUT(1) << "Tcl error while creating Tcl-interpreter (" << id << "): " << e.what() << std::endl; } 279 catch (const std::exception& e) 280 { newbundle->interpreter_ = 0; COUT(1) << "Error while creating Tcl-interpreter (" << id << "): " << e.what() << std::endl; } 281 catch (...) 282 { newbundle->interpreter_ = 0; COUT(1) << "An error occurred while creating a new Tcl-interpreter (" << id << ")" << std::endl; } 283 284 { 285 // Add the new bundle to the map 286 boost::unique_lock<boost::shared_mutex> lock(*TclThreadManager::getInstance().interpreterBundlesMutex_); 287 TclThreadManager::getInstance().interpreterBundles_[id] = newbundle; 288 } 289 290 return newbundle->interpreter_; 291 } 292 293 /** 294 @brief Stops and destroys a given Tcl-interpreter 295 */ 296 void TclThreadManager::destroy(unsigned int id) 297 { 298 // TODO 299 // Not yet implemented 300 } 301 302 /** 303 @brief Sends a command to the queue of a given Tcl-interpreter 304 @param id The id of the target interpreter 305 @param command The command to be sent 306 */ 307 void TclThreadManager::execute(unsigned int target_id, const std::string& command) 308 { 309 TclThreadManager::getInstance()._execute(target_id, command); 310 } 311 312 /** 313 @brief This function can be called from Tcl to execute a console command. 314 315 Commands which shall be executed are put into a queue and processed as soon as the 316 main thread feels ready to do so. The queue may also be full which results in a temporary 317 suspension of the calling thread until the queue gets ready again. 318 */ 319 void TclThreadManager::tcl_execute(const Tcl::object& args) 320 { 321 TclThreadManager::getInstance()._execute(0, stripEnclosingBraces(args.get())); 322 } 323 324 /** 325 @brief This function can be called from Tcl to send a command to the queue of any interpreter. 326 @param target_id The id of the target thread 327 */ 328 void TclThreadManager::tcl_crossexecute(int target_id, const Tcl::object& args) 329 { 330 TclThreadManager::getInstance()._execute(static_cast<unsigned int>(target_id), stripEnclosingBraces(args.get())); 331 } 332 333 /** 334 @brief Sends a command to the queue of a given Tcl-interpreter 335 @param id The id of the target interpreter 336 @param command The command to be sent 337 */ 338 void TclThreadManager::_execute(unsigned int target_id, const std::string& command) 339 { 340 TclInterpreterBundle* bundle = this->getInterpreterBundle(target_id); 341 if (bundle) 342 bundle->queue_.push_back(command); 343 } 344 345 346 /** 347 @brief Sends a query to a given Tcl-interpreter and waits for the result 348 @param id The id of the target interpreter 349 @param command The command to be sent 350 @return The result of the command 351 */ 352 std::string TclThreadManager::query(unsigned int target_id, const std::string& command) 353 { 354 return TclThreadManager::getInstance()._query(0, target_id, command); 355 } 356 357 /** 358 @brief This function can be called from Tcl to send a query to the main thread. 359 @param source_id The id of the calling thread 360 361 A query waits for the result of the command. This means, the calling thread will be blocked until 362 the main thread answers the query. In return, the main thread sends the result of the console 363 command back to Tcl. 364 */ 365 std::string TclThreadManager::tcl_query(int source_id, const Tcl::object& args) 366 { 367 return TclThreadManager::getInstance()._query(static_cast<unsigned int>(source_id), 0, stripEnclosingBraces(args.get()), true); 368 } 369 370 /** 371 @brief This function can be called from Tcl to send a query to another thread. 372 @param source_id The id of the calling thread 373 @param target_id The id of the target thread 374 */ 375 std::string TclThreadManager::tcl_crossquery(int source_id, int target_id, const Tcl::object& args) 376 { 377 return TclThreadManager::getInstance()._query(static_cast<unsigned int>(source_id), static_cast<unsigned int>(target_id), stripEnclosingBraces(args.get())); 378 } 379 380 /** 381 @brief This function performs a query to any Tcl interpreter 382 @param source_id The id of the calling thread 383 @param target_id The id of the target thread 384 @param command The command to send as a query 385 @param bUseCommandExecutor Only used if the target_id is 0 (which references the main interpreter). In this case it means if the command should be passed to the CommandExecutor (true) or to the main Tcl interpreter (false). This is true when called by tcl_query and false when called by tcl_crossquery. 386 */ 387 std::string TclThreadManager::_query(unsigned int source_id, unsigned int target_id, const std::string& command, bool bUseCommandExecutor) 388 { 389 TclInterpreterBundle* source_bundle = this->getInterpreterBundle(source_id); 390 TclInterpreterBundle* target_bundle = this->getInterpreterBundle(target_id); 391 std::string output; 392 393 if (source_bundle && target_bundle) 394 { 395 // At this point we assume the mutex of source_bundle to be locked (because it's executing this query right now an waits for the return value) 396 // We can safely use it's querier list (because there's no other place in the code using the list except this query - and the interpreter can't start more than one query) 397 398 if ((source_bundle->id_ == target_bundle->id_) || source_bundle->queriers_.is_in(target_bundle->id_)) 399 { 400 // This query would lead to a deadlock - return with an error 401 this->error("Error: Circular query (" + this->dumpList(source_bundle->queriers_.getList()) + " " + getConvertedValue<unsigned int, std::string>(source_bundle->id_) \ 402 + " -> " + getConvertedValue<unsigned int, std::string>(target_bundle->id_) \ 403 + "), couldn't query Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(target_bundle->id_) \ 404 + " from other interpreter with ID " + getConvertedValue<unsigned int, std::string>(source_bundle->id_) + "."); 405 } 406 else 407 { 408 boost::unique_lock<boost::mutex> lock(target_bundle->mutex_, boost::try_to_lock); 409 boost::unique_lock<boost::mutex> mainlock(*this->mainInterpreterMutex_, boost::defer_lock); 410 411 if (!lock.owns_lock() && source_bundle->id_ != 0) 412 { 413 // We couldn't obtain the try_lock immediately and we're not the main interpreter - wait until the lock becomes possible (note: the main interpreter won't wait and instead returns an error - see below) 414 if (target_bundle->id_ == 0) 549 boost::mutex::scoped_try_lock interpreter_lock(target->interpreterMutex_); 550 bool successfullyLocked = false; 551 try 415 552 { 416 // We're querying the main interpreter - use the main interpreter mutex to synchronize 417 mainlock.lock(); 418 lock.lock(); 553 if (querierID == 0 || std::find(querier->queriers_.begin(), querier->queriers_.end(), 0U) != querier->queriers_.end()) 554 successfullyLocked = interpreter_lock.try_lock(); 555 else 556 { 557 while (!interpreter_lock.try_lock()) 558 { 559 boost::this_thread::yield(); 560 } 561 562 successfullyLocked = true; 563 } 564 } catch (...) {} 565 566 if (successfullyLocked) 567 { 568 this->debug("TclThread_query: " + command); 569 try 570 { output = static_cast<std::string>(target->interpreter_->eval(command)); } 571 catch (Tcl::tcl_error const &e) 572 { this->error("Tcl error: " + static_cast<std::string>(e.what())); } 573 catch (std::exception const &e) 574 { this->error("Error while executing Tcl: " + static_cast<std::string>(e.what())); } 419 575 } 420 576 else 421 577 { 422 // We're querying a threaded interpreter - no synchronization needed 423 lock.lock(); 578 this->error("Error: Couldn't query Tcl-interpreter with ID " + multi_cast<std::string>(threadID) + ", interpreter is busy right now."); 424 579 } 425 580 } 426 581 427 if (lock.owns_lock()) 582 boost::mutex::scoped_lock queriers_lock(target->queriersMutex_); 583 target->queriers_.clear(); 584 } 585 } 586 return output; 587 } 588 589 void TclThreadManager::update(const Clock& time) 590 { 591 { 592 orxonoxEvalCondition_g.notify_one(); 593 boost::this_thread::yield(); 594 } 595 596 { 597 boost::mutex::scoped_lock bundles_lock(bundlesMutex_g); 598 for (std::map<unsigned int, TclInterpreterBundle*>::iterator it = this->interpreterBundles_.begin(); it != this->interpreterBundles_.end(); ++it) 599 { 600 boost::mutex::scoped_lock queue_lock((*it).second->queueMutex_); 601 if (!(*it).second->queue_.empty()) 428 602 { 429 // Now the mutex of target_bundle is also locked an we can update the querier list 430 target_bundle->queriers_.insert(target_bundle->queriers_.getList().begin(), source_bundle->queriers_.getList().begin(), source_bundle->queriers_.getList().end()); 431 target_bundle->queriers_.push_back(source_bundle->id_); 432 433 // Perform the query (note: this happens in the main thread because we need the returnvalue) 434 if (target_bundle->id_ == 0 && bUseCommandExecutor) 603 std::string command = (*it).second->queue_.front(); 604 (*it).second->queue_.pop_front(); 435 605 { 436 // It's a query to the CommandExecutor 437 this->debug("TclThread_query -> CE: " + command); 438 if (!CommandExecutor::execute(command, false)) 439 this->error("Error: Can't execute command \"" + command + "\"!"); 440 441 if (CommandExecutor::getLastEvaluation().hasReturnvalue()) 442 output = CommandExecutor::getLastEvaluation().getReturnvalue().getString(); 606 boost::mutex::scoped_lock finished_lock((*it).second->finishedMutex_); 607 (*it).second->finished_ = false; 443 608 } 444 else 445 { 446 // It's a query to a Tcl interpreter 447 this->debug("TclThread_query: " + command); 448 449 output = this->eval(target_bundle, command); 450 } 451 452 // Clear the queriers list of the target 453 target_bundle->queriers_.clear(); 454 455 // Unlock the mutex of the target_bundle 456 lock.unlock(); 457 458 // Finally unlock the main interpreter lock if necessary 459 if (mainlock.owns_lock()) 460 mainlock.unlock(); 609 boost::thread(boost::bind(&tclThread, (*it).second, command)); 461 610 } 462 else 463 { 464 // This happens if the main thread tries to query a busy interpreter 465 // To avoid a lock of the main thread, we simply don't proceed with the query in this case 466 this->error("Error: Couldn't query Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(target_bundle->id_) + ", interpreter is busy right now."); 467 } 468 } 469 470 } 471 472 return output; 473 } 474 475 /** 476 @brief This function can be called from Tcl to ask if the thread is still suposed to be running. 477 @param id The id of the thread in question 478 */ 479 bool TclThreadManager::tcl_running(int id) 480 { 481 TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(static_cast<unsigned int>(id)); 482 if (bundle) 483 return bundle->bRunning_; 484 else 485 return false; 486 } 487 488 /** 489 @brief Returns the interpreter bundle with the given id. 490 @param id The id of the interpreter 491 @return The interpreter or 0 if the id doesn't exist 492 */ 493 TclInterpreterBundle* TclThreadManager::getInterpreterBundle(unsigned int id) 494 { 495 boost::shared_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_); 496 497 std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = this->interpreterBundles_.find(id); 498 if (it != this->interpreterBundles_.end()) 499 { 500 return it->second; 501 } 502 else 503 { 504 this->error("Error: No Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(id) + " existing."); 505 return 0; 506 } 507 } 508 509 /** 510 @brief Returns a string containing all elements of a unsigned-integer-list separated by spaces. 511 */ 512 std::string TclThreadManager::dumpList(const std::list<unsigned int>& list) 513 { 514 std::string output = ""; 515 for (std::list<unsigned int>::const_iterator it = list.begin(); it != list.end(); ++it) 516 { 517 if (it != list.begin()) 518 output += " "; 519 520 output += getConvertedValue<unsigned int, std::string>(*it); 521 } 522 return output; 523 } 524 525 /** 526 @brief Returns a list with the numbers of all existing Tcl-interpreters. 527 528 This function is used by the auto completion function. 529 */ 611 } 612 } 613 614 { 615 boost::mutex::scoped_lock interpreter_lock(this->orxonoxInterpreterBundle_->interpreterMutex_); 616 unsigned long maxtime = static_cast<unsigned long>(time.getDeltaTime() * 1000000 * TCLTHREADMANAGER_MAX_CPU_USAGE); 617 Ogre::Timer timer; 618 while (!this->queueIsEmpty()) 619 { 620 CommandExecutor::execute(this->popCommandFromQueue(), false); 621 if (timer.getMicroseconds() > maxtime) 622 break; 623 } 624 } 625 } 626 530 627 std::list<unsigned int> TclThreadManager::getThreadList() const 531 628 { 532 boost::shared_lock<boost::shared_mutex> lock(*this->interpreterBundlesMutex_); 533 629 boost::mutex::scoped_lock bundles_lock(bundlesMutex_g); 534 630 std::list<unsigned int> threads; 535 631 for (std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = this->interpreterBundles_.begin(); it != this->interpreterBundles_.end(); ++it) 536 if (it->first > 0 && it->first <= this->numInterpreterBundles_) // only list autonumbered interpreters (created with create()) - exclude the default interpreter 0 and all manually numbered interpreters) 537 threads.push_back(it->first); 632 threads.push_back((*it).first); 538 633 return threads; 539 634 } 540 635 541 /** 542 @brief A helper function to print errors in a thread safe manner. 543 */ 544 void TclThreadManager::error(const std::string& error) 545 { 546 this->messageQueue_->push_back("error " + error); 547 } 548 549 /** 550 @brief A helper function to print debug information in a thread safe manner. 551 */ 552 void TclThreadManager::debug(const std::string& error) 553 { 554 this->messageQueue_->push_back("debug " + error); 555 } 556 557 /** 558 @brief Evaluates a Tcl command without throwing exceptions (which may rise problems on certain machines). 559 @return The Tcl return value 560 561 Errors are reported through the @ref error function. 562 */ 563 std::string TclThreadManager::eval(TclInterpreterBundle* bundle, const std::string& command) 564 { 565 Tcl_Interp* interpreter = bundle->interpreter_->get(); 566 int cc = Tcl_Eval(interpreter, command.c_str()); 567 568 Tcl::details::result result(interpreter); 569 570 if (cc != TCL_OK) 571 { 572 this->error("Tcl error (execute, ID " + getConvertedValue<unsigned int, std::string>(bundle->id_) + "): " + static_cast<std::string>(result)); 573 return ""; 574 } 575 else 576 { 577 return result; 578 } 579 } 580 581 //////////////// 582 // The Thread // 583 //////////////// 584 585 /** 586 @brief The main function of the thread. Executes a Tcl command. 587 @param bundle The interpreter bundle containing all necessary variables 588 @param command the Command to execute 589 */ 590 void tclThread(TclInterpreterBundle* bundle, std::string command) 636 void tclThread(TclInterpreterBundle* interpreterBundle, std::string command) 591 637 { 592 638 TclThreadManager::getInstance().debug("TclThread_execute: " + command); 593 594 TclThreadManager::getInstance().eval(bundle, command); 595 596 bundle->lock_->unlock(); 639 boost::mutex::scoped_lock interpreter_lock(interpreterBundle->interpreterMutex_); 640 try 641 { 642 interpreterBundle->interpreter_->eval(command); 643 } 644 catch (Tcl::tcl_error const &e) 645 { 646 TclThreadManager::getInstance().error("Tcl (ID " + multi_cast<std::string>(interpreterBundle->id_) + ") error: " + e.what()); 647 } 648 catch (std::exception const &e) 649 { 650 TclThreadManager::getInstance().error("Error while executing Tcl (ID " + multi_cast<std::string>(interpreterBundle->id_) + "): " + e.what()); 651 } 652 653 boost::mutex::scoped_lock finished_lock(interpreterBundle->finishedMutex_); 654 interpreterBundle->finished_ = true; 655 interpreterBundle->finishedCondition_.notify_all(); 597 656 } 598 657 } -
code/trunk/src/core/TclThreadManager.h
r3308 r3313 33 33 34 34 #include <cassert> 35 #include <list> 35 36 #include <map> 36 37 #include <string> 37 38 #include "OrxonoxClass.h" 38 #include "core/OrxonoxClass.h" 39 39 40 40 namespace orxonox 41 41 { 42 // Internal struct 43 struct TclInterpreterBundle; 44 42 45 class _CoreExport TclThreadManager : public OrxonoxClass 43 46 { 47 friend class IRC; 44 48 friend class TclBind; 45 friend void tclThread(TclInterpreterBundle* bundle, std::string command);46 49 47 50 public: 48 51 TclThreadManager(Tcl::interpreter* interpreter); 49 virtual~TclThreadManager();52 ~TclThreadManager(); 50 53 51 static TclThreadManager& getInstance() { assert( TclThreadManager::singletonPtr_s); return *TclThreadManager::singletonPtr_s; }54 static TclThreadManager& getInstance() { assert(singletonRef_s); return *singletonRef_s; } 52 55 53 static unsigned int create(); 54 static Tcl::interpreter* createWithId(unsigned int id); 55 static void destroy(unsigned int id); 56 static void execute(unsigned int target_id, const std::string& command); 57 static std::string query(unsigned int target_id, const std::string& command); 56 static unsigned int create(); 57 static unsigned int createID(unsigned int threadID); 58 static void destroy(unsigned int threadID); 59 static void execute(unsigned int threadID, const std::string& command); 60 static std::string query(unsigned int threadID, const std::string& command); 61 static void status(); 62 static void dump(unsigned int threadID); 63 static void flush(unsigned int threadID); 58 64 59 65 void error(const std::string& error); … … 65 71 66 72 private: 67 static void tcl_execute(const Tcl::object& args); 68 static void tcl_crossexecute(int target_id, const Tcl::object& args); 69 static std::string tcl_query(int source_id, const Tcl::object& args); 70 static std::string tcl_crossquery(int source_id, int target_id, const Tcl::object& args); 71 static bool tcl_running(int id); 73 TclThreadManager(const TclThreadManager& other); 72 74 73 void _execute(unsigned int target_id, const std::string& command); 74 std::string _query(unsigned int source_id, unsigned int target_id, const std::string& command, bool bUseCommandExecutor = false); 75 static void tcl_execute(Tcl::object const &args); 76 static std::string tcl_query(int querierID, Tcl::object const &args); 77 static std::string tcl_crossquery(int querierID, int threadID, Tcl::object const &args); 78 static bool tcl_running(int threadID); 75 79 76 TclInterpreterBundle* getInterpreterBundle(unsigned int id); 80 Tcl::interpreter* createNewTclInterpreter(const std::string& threadID); 81 Tcl::interpreter* getTclInterpreter(unsigned int threadID); 82 TclInterpreterBundle* getInterpreterBundle(unsigned int threadID); 77 83 std::string dumpList(const std::list<unsigned int>& list); 78 84 79 std::string eval(TclInterpreterBundle* bundle, const std::string& command); 85 void pushCommandToQueue(const std::string& command); 86 void forceCommandToFrontOfQueue(const std::string& command); 87 std::string popCommandFromQueue(); 88 bool queueIsEmpty(); 80 89 81 static TclThreadManager* singletonPtr_s; ///< Singleton pointer 90 void pushCommandToQueue(unsigned int threadID, const std::string& command); 91 std::string popCommandFromQueue(unsigned int threadID); 92 bool queueIsEmpty(unsigned int threadID); 82 93 83 unsigned int numInterpreterBundles_; ///< Number of created Tcl-interpreters (only used for auto-numbered interpreters, not affected by @ref createWithId) 84 std::map<unsigned int, TclInterpreterBundle*> interpreterBundles_; ///< A map containing all Tcl-interpreters 85 boost::shared_mutex* interpreterBundlesMutex_; ///< A mutex used to synchronize threads when accessing @ref interpreterBundles_ 86 TclThreadList<std::string>* messageQueue_; ///< A queue to pass messages from Tcl-threads to the main thread 87 boost::mutex* mainInterpreterMutex_; ///< A mutex to synchronize queries to the main interpreter 94 bool updateQueriersList(TclInterpreterBundle* querier, TclInterpreterBundle* target); 95 96 std::string evalQuery(unsigned int querierID, const std::string& command); 97 std::string evalQuery(unsigned int querierID, unsigned int threadID, const std::string& command); 98 99 unsigned int threadCounter_; 100 TclInterpreterBundle* orxonoxInterpreterBundle_; 101 std::map<unsigned int, TclInterpreterBundle*> interpreterBundles_; 102 103 static TclThreadManager* singletonRef_s; 88 104 }; 89 105 90 _CoreExport void tclThread(TclInterpreterBundle* bundle, std::string command);106 _CoreExport void tclThread(TclInterpreterBundle* interpreterBundle, std::string command); 91 107 } 92 108
Note: See TracChangeset
for help on using the changeset viewer.