Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * exec_thread.cpp - Fawkes Skiller: Execution Thread 00004 * 00005 * Created: Mon Feb 18 10:30:17 2008 00006 * Copyright 2006-2009 Tim Niemueller [www.niemueller.de] 00007 * 00008 ****************************************************************************/ 00009 00010 /* This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. 00014 * 00015 * This program is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 * GNU Library General Public License for more details. 00019 * 00020 * Read the full text in the LICENSE.GPL file in the doc directory. 00021 */ 00022 00023 #include "exec_thread.h" 00024 00025 #include <core/exceptions/software.h> 00026 #include <core/exceptions/system.h> 00027 #include <core/threading/mutex.h> 00028 #include <logging/component.h> 00029 #ifdef SKILLER_TIMETRACKING 00030 # include <utils/time/tracker.h> 00031 #endif 00032 00033 #include <lua/context.h> 00034 #include <lua/interface_importer.h> 00035 00036 #include <interfaces/SkillerInterface.h> 00037 #include <interfaces/SkillerDebugInterface.h> 00038 00039 #include <lua.hpp> 00040 #include <tolua++.h> 00041 00042 #include <string> 00043 #include <cstring> 00044 00045 using namespace std; 00046 using namespace fawkes; 00047 00048 /** @class SkillerExecutionThread "exec_thread.h" 00049 * Skiller Execution Thread. 00050 * This thread runs and controls the Lua interpreter and passes data into the 00051 * execution engine. 00052 * 00053 * @author Tim Niemueller 00054 */ 00055 00056 /** Constructor. */ 00057 SkillerExecutionThread::SkillerExecutionThread() 00058 : Thread("SkillerExecutionThread", Thread::OPMODE_WAITFORWAKEUP), 00059 BlockedTimingAspect(BlockedTimingAspect::WAKEUP_HOOK_SKILL), 00060 BlackBoardInterfaceListener("SkillerExecutionThread") 00061 { 00062 __continuous_run = false; 00063 __continuous_reset = false; 00064 __error_written = false; 00065 00066 __lua = NULL; 00067 } 00068 00069 00070 /** Destructor. */ 00071 SkillerExecutionThread::~SkillerExecutionThread() 00072 { 00073 } 00074 00075 /** Clean up when init failed. 00076 * You may only call this from init(). Never ever call it from anywhere 00077 * else! 00078 */ 00079 void 00080 SkillerExecutionThread::init_failure_cleanup() 00081 { 00082 try { 00083 if ( __skiller_if ) blackboard->close(__skiller_if); 00084 if ( __skdbg_if ) blackboard->close(__skdbg_if); 00085 00086 delete __lua_ifi; 00087 delete __clog; 00088 00089 } catch (...) { 00090 // we really screwed up, can't do anything about it, ignore error, logger is 00091 // initialized since this method is only called from init() which is only called if 00092 // all aspects had been initialized successfully 00093 logger->log_error(name(), "Really screwed up while finalizing, aborting cleanup. " 00094 "Fawkes is no longer in a clean state. Restart!"); 00095 } 00096 } 00097 00098 00099 void 00100 SkillerExecutionThread::init() 00101 { 00102 __last_exclusive_controller = 0; 00103 __reader_just_left = false; 00104 __continuous_run = false; 00105 __continuous_reset = false; 00106 __skdbg_what = "ACTIVE"; 00107 __skdbg_graphdir = "TB"; 00108 __skdbg_graphcolored = true; 00109 __clog = NULL; 00110 __sksf_pushed = false; 00111 00112 try { 00113 __cfg_skillspace = config->get_string("/skiller/skillspace"); 00114 __cfg_watch_files = config->get_bool("/skiller/watch_files"); 00115 } catch (Exception &e) { 00116 e.append("Insufficient configuration for Skiller"); 00117 throw; 00118 } 00119 00120 logger->log_debug("SkillerExecutionThread", "Skill space: %s", __cfg_skillspace.c_str()); 00121 00122 __clog = new ComponentLogger(logger, "SkillerLua"); 00123 00124 __lua = NULL; 00125 __lua_ifi = NULL; 00126 __skiller_if = NULL; 00127 __skdbg_if = NULL; 00128 00129 std::string reading_prefix = "/skiller/interfaces/" + __cfg_skillspace + "/reading/"; 00130 std::string writing_prefix = "/skiller/interfaces/" + __cfg_skillspace + "/writing/"; 00131 00132 try { 00133 __skiller_if = blackboard->open_for_writing<SkillerInterface>("Skiller"); 00134 __skdbg_if = blackboard->open_for_writing<SkillerDebugInterface>("Skiller"); 00135 00136 __lua = new LuaContext(); 00137 if (__cfg_watch_files) { 00138 __lua->setup_fam(/* auto restart */ true, /* conc thread */ false); 00139 } 00140 00141 __lua_ifi = new LuaInterfaceImporter(__lua, blackboard, config, logger); 00142 __lua_ifi->open_reading_interfaces(reading_prefix); 00143 __lua_ifi->open_writing_interfaces(writing_prefix); 00144 __lua_ifi->add_interface("skdbg", __skdbg_if); 00145 __lua_ifi->add_interface("skiller", __skiller_if); 00146 00147 __lua->add_package_dir(LUADIR); 00148 __lua->add_cpackage_dir(LUALIBDIR); 00149 00150 __lua->add_package("fawkesutils"); 00151 __lua->add_package("fawkesconfig"); 00152 __lua->add_package("fawkeslogging"); 00153 __lua->add_package("fawkesinterface"); 00154 __lua->add_package("fawkesgeometry"); 00155 #ifdef HAVE_TF 00156 __lua->add_package("fawkestf"); 00157 #endif 00158 00159 __lua->set_string("SKILLSPACE", __cfg_skillspace.c_str()); 00160 __lua->set_usertype("config", config, "Configuration", "fawkes"); 00161 __lua->set_usertype("logger", __clog, "ComponentLogger", "fawkes"); 00162 __lua->set_usertype("clock", clock, "Clock", "fawkes"); 00163 #ifdef HAVE_TF 00164 __lua->set_usertype("tf", tf_listener, "Transformer", "fawkes::tf"); 00165 #endif 00166 00167 __lua_ifi->push_interfaces(); 00168 00169 __lua->set_start_script(LUADIR"/skiller/start.lua"); 00170 00171 __skiller_if->set_skill_string(""); 00172 __skiller_if->set_status(SkillerInterface::S_INACTIVE); 00173 __skiller_if->write(); 00174 00175 __skdbg_if->set_graph(""); 00176 __skdbg_if->set_graph_fsm("ACTIVE"); 00177 00178 } catch (Exception &e) { 00179 init_failure_cleanup(); 00180 throw; 00181 } 00182 00183 // We want to know if our reader leaves and closes the interface 00184 bbil_add_reader_interface(__skiller_if); 00185 blackboard->register_listener(this); 00186 00187 #ifdef SKILLER_TIMETRACKING 00188 __tt = new TimeTracker(); 00189 __ttc_total = __tt->add_class("Total"); 00190 __ttc_msgproc = __tt->add_class("Message Processing"); 00191 __ttc_luaprep = __tt->add_class("Lua Preparation"); 00192 __ttc_luaexec = __tt->add_class("Lua Execution"); 00193 __ttc_publish = __tt->add_class("Publishing"); 00194 __tt_loopcount = 0; 00195 #endif 00196 } 00197 00198 00199 void 00200 SkillerExecutionThread::finalize() 00201 { 00202 #ifdef SKILLER_TIMETRACKING 00203 delete __tt; 00204 #endif 00205 delete __lua_ifi; 00206 00207 blackboard->unregister_listener(this); 00208 blackboard->close(__skiller_if); 00209 blackboard->close(__skdbg_if); 00210 00211 delete __lua; 00212 delete __clog; 00213 } 00214 00215 00216 void 00217 SkillerExecutionThread::bb_interface_reader_removed(Interface *interface, 00218 unsigned int instance_serial) throw() 00219 { 00220 if ( instance_serial == __skiller_if->exclusive_controller() ) { 00221 logger->log_debug("SkillerExecutionThread", "Controlling interface instance was closed, " 00222 "revoking exclusive control"); 00223 00224 __last_exclusive_controller = instance_serial; 00225 __reader_just_left = true; 00226 00227 __skiller_if->set_exclusive_controller(0); 00228 __skiller_if->write(); 00229 } 00230 } 00231 00232 00233 00234 /** Determines the skill status and writes it to the BB. 00235 * This method assumes that it is called from within loop() and lua_mutex is locked. 00236 * @param curss current skill string 00237 */ 00238 void 00239 SkillerExecutionThread::publish_skill_status(std::string &curss) 00240 { 00241 //const char *sst = "Unknown"; 00242 LUA_INTEGER running = 0, final = 0, failed = 0; 00243 00244 SkillerInterface::SkillStatusEnum old_status = __skiller_if->status(); 00245 SkillerInterface::SkillStatusEnum new_status = __skiller_if->status(); 00246 00247 try { 00248 00249 if ( curss == "" ) { 00250 // nothing running, we're inactive 00251 //sst = "S_INACTIVE/empty"; 00252 __skiller_if->set_status(SkillerInterface::S_INACTIVE); 00253 00254 } else { // Stack: 00255 __lua->get_global("skillenv"); // skillenv 00256 00257 __lua->get_field(-1, "get_status"); // skillenv skillenv.get_status 00258 if ( __lua->is_function(-1) ) { 00259 __lua->pcall(0, 3); // skillenv running final failed 00260 running = __lua->to_integer(-3); 00261 final = __lua->to_integer(-2); 00262 failed = __lua->to_integer(-1); 00263 00264 __lua->pop(4); // --- 00265 } else { 00266 __lua->pop(2); // --- 00267 throw LuaRuntimeException("C++:publish_skill_status", "skillenv.get_status is not a function"); 00268 } 00269 00270 if ( failed > 0 ) { 00271 //sst = "S_FAILED"; 00272 new_status = SkillerInterface::S_FAILED; 00273 } else if ( (final > 0) && (running == 0) ) { 00274 //sst = "S_FINAL"; 00275 new_status = SkillerInterface::S_FINAL; 00276 } else if ( running > 0 ) { 00277 //sst = "S_RUNNING"; 00278 new_status = SkillerInterface::S_RUNNING; 00279 } else { 00280 // all zero 00281 //sst = "S_INACTIVE"; 00282 new_status = SkillerInterface::S_INACTIVE; 00283 } 00284 } 00285 00286 if ( (old_status != new_status) || 00287 (curss != __skiller_if->skill_string()) || 00288 (__skiller_if->is_continuous() != __continuous_run) ) { 00289 00290 /* 00291 logger->log_debug("SkillerExecutionThread", "Status is %s (%i vs. %i)" 00292 "(running %i, final: %i, failed: %i)", 00293 sst, old_status, new_status, running, final, failed); 00294 */ 00295 00296 __skiller_if->set_skill_string(curss.c_str()); 00297 __skiller_if->set_continuous(__continuous_run); 00298 00299 __skiller_if->set_status(new_status); 00300 00301 if ( ! __error_written && (new_status == SkillerInterface::S_FAILED) ) { 00302 publish_error(); 00303 __error_written = __continuous_run; 00304 } else if (new_status == SkillerInterface::S_RUNNING || 00305 new_status == SkillerInterface::S_FINAL) { 00306 __skiller_if->set_error(""); 00307 __error_written = false; 00308 } 00309 00310 __skiller_if->write(); 00311 } 00312 00313 } catch (Exception &e) { 00314 logger->log_error("SkillerExecutionThread", "Failed to retrieve skill status"); 00315 logger->log_error("SkillerExecutionThread", e); 00316 try { 00317 __skiller_if->set_status(SkillerInterface::S_FAILED); 00318 } catch (Exception &e2) { 00319 logger->log_error("SkillerExecutionThread", "Failed to set FAILED as skill " 00320 "status value during error handling"); 00321 logger->log_error("SkillerExecutionThread", e2); 00322 } 00323 } 00324 00325 } 00326 00327 00328 void 00329 SkillerExecutionThread::publish_skdbg() 00330 { 00331 try { 00332 __lua->do_string("skillenv.write_skiller_debug(interfaces.writing.skdbg, \"%s\", \"%s\", %s)", 00333 __skdbg_what.c_str(), __skdbg_graphdir.c_str(), 00334 __skdbg_graphcolored ? "true" : "false"); 00335 } catch (Exception &e) { 00336 logger->log_warn("SkillerExecutionThread", "Error writing graph"); 00337 logger->log_warn("SkillerExecutionThread", e); 00338 } 00339 } 00340 00341 void 00342 SkillerExecutionThread::lua_loop_reset() 00343 { 00344 try { 00345 __lua->do_string("skillenv.reset_loop()"); 00346 } catch (Exception &e) { 00347 logger->log_warn("SkillerExecutionThread", "Lua Loop Reset failed"); 00348 logger->log_warn("SkillerExecutionThread", e); 00349 } 00350 } 00351 00352 00353 void 00354 SkillerExecutionThread::publish_error() 00355 { 00356 try { 00357 __lua->do_string("skillenv.write_fsm_error(skillenv.get_skill_fsm(skillenv.get_active_skills()), interfaces.writing.skiller)"); 00358 } catch (Exception &e) { 00359 logger->log_warn("SkillerExecutionThread", "Error writing error"); 00360 logger->log_warn("SkillerExecutionThread", e); 00361 __skiller_if->set_error("Failed to set Lua error"); 00362 __skiller_if->write(); 00363 } 00364 } 00365 00366 00367 void 00368 SkillerExecutionThread::process_skdbg_messages() 00369 { 00370 while ( ! __skdbg_if->msgq_empty() ) { 00371 if ( __skdbg_if->msgq_first_is<SkillerDebugInterface::SetGraphMessage>() ) { 00372 SkillerDebugInterface::SetGraphMessage *m = __skdbg_if->msgq_first<SkillerDebugInterface::SetGraphMessage>(); 00373 logger->log_warn(name(), "Setting skiller debug what to: %s", m->graph_fsm()); 00374 __skdbg_what = m->graph_fsm(); 00375 } else if (__skdbg_if->msgq_first_is<SkillerDebugInterface::SetGraphDirectionMessage>() ) { 00376 SkillerDebugInterface::SetGraphDirectionMessage *m = __skdbg_if->msgq_first<SkillerDebugInterface::SetGraphDirectionMessage>(); 00377 switch (m->graph_dir()) { 00378 case SkillerDebugInterface::GD_BOTTOM_TOP: __skdbg_graphdir = "BT"; break; 00379 case SkillerDebugInterface::GD_LEFT_RIGHT: __skdbg_graphdir = "LR"; break; 00380 case SkillerDebugInterface::GD_RIGHT_LEFT: __skdbg_graphdir = "RL"; break; 00381 default: __skdbg_graphdir = "TB"; break; 00382 } 00383 00384 } else if (__skdbg_if->msgq_first_is<SkillerDebugInterface::SetGraphColoredMessage>() ) { 00385 SkillerDebugInterface::SetGraphColoredMessage *m = __skdbg_if->msgq_first<SkillerDebugInterface::SetGraphColoredMessage>(); 00386 __skdbg_graphcolored = m->is_graph_colored(); 00387 } 00388 00389 __skdbg_if->msgq_pop(); 00390 } 00391 } 00392 00393 00394 void 00395 SkillerExecutionThread::loop() 00396 { 00397 #ifdef SKILLER_TIMETRACKING 00398 __tt->ping_start(__ttc_total); 00399 #endif 00400 #ifdef HAVE_INOTIFY 00401 __lua->process_fam_events(); 00402 #endif 00403 __lua_ifi->read(); 00404 00405 // Current skill string 00406 std::string curss = ""; 00407 00408 unsigned int excl_ctrl = __skiller_if->exclusive_controller(); 00409 bool write_skiller_if = false; 00410 bool last_was_continuous = __continuous_run; 00411 00412 #ifdef SKILLER_TIMETRACKING 00413 __tt->ping_start(__ttc_msgproc); 00414 #endif 00415 process_skdbg_messages(); 00416 00417 while ( ! __skiller_if->msgq_empty() ) { 00418 if ( __skiller_if->msgq_first_is<SkillerInterface::AcquireControlMessage>() ) { 00419 Message *m = __skiller_if->msgq_first(); 00420 if ( excl_ctrl == 0 ) { 00421 logger->log_debug("SkillerExecutionThread", "%s is new exclusive controller", 00422 m->sender_thread_name()); 00423 __skiller_if->set_exclusive_controller(m->sender_id()); 00424 write_skiller_if = true; 00425 excl_ctrl = m->sender_id(); 00426 } else if (excl_ctrl == m->sender_id()) { 00427 // ignored 00428 } else { 00429 logger->log_warn("SkillerExecutionThread", "%s tried to acquire " 00430 "exclusive control, but another controller exists " 00431 "already", m->sender_thread_name()); 00432 } 00433 00434 } else if ( __skiller_if->msgq_first_is<SkillerInterface::ReleaseControlMessage>() ) { 00435 Message *m = __skiller_if->msgq_first(); 00436 if ( excl_ctrl == m->sender_id() ) { 00437 logger->log_debug("SkillerExecutionThread", "%s releases exclusive control", 00438 m->sender_thread_name()); 00439 00440 if ( __continuous_run ) { 00441 __continuous_run = false; 00442 __continuous_reset = true; 00443 } 00444 __last_exclusive_controller = __skiller_if->exclusive_controller(); 00445 __skiller_if->set_exclusive_controller(0); 00446 write_skiller_if = true; 00447 excl_ctrl = 0; 00448 } else { 00449 if ( !__reader_just_left || (m->sender_id() != __last_exclusive_controller)) { 00450 logger->log_warn("SkillerExecutionThread", "%s tried to release exclusive control, " 00451 "it's not the controller", m->sender_thread_name()); 00452 } 00453 } 00454 } else if ( __skiller_if->msgq_first_is<SkillerInterface::ExecSkillMessage>() ) { 00455 SkillerInterface::ExecSkillMessage *m = __skiller_if->msgq_first<SkillerInterface::ExecSkillMessage>(); 00456 00457 if ( m->sender_id() == excl_ctrl ) { 00458 if ( curss != "" ) { 00459 logger->log_warn("SkillerExecutionThread", "More than one skill string enqueued, " 00460 "ignoring previous string (%s).", curss.c_str()); 00461 } 00462 logger->log_debug("SkillerExecutionThread", "%s wants me to execute '%s'", 00463 m->sender_thread_name(), m->skill_string()); 00464 00465 if ( __continuous_run ) { 00466 __continuous_run = false; 00467 __continuous_reset = true; 00468 } 00469 curss = m->skill_string(); 00470 } else { 00471 logger->log_debug("SkillerExecutionThread", "%s tries to exec while not controller", 00472 m->sender_thread_name()); 00473 } 00474 00475 } else if ( __skiller_if->msgq_first_is<SkillerInterface::ExecSkillContinuousMessage>() ) { 00476 SkillerInterface::ExecSkillContinuousMessage *m = __skiller_if->msgq_first<SkillerInterface::ExecSkillContinuousMessage>(); 00477 00478 if ( m->sender_id() == excl_ctrl ) { 00479 if ( curss != "" ) { 00480 logger->log_warn("SkillerExecutionThread", "More than one skill string enqueued, " 00481 "ignoring successive string (%s).", m->skill_string()); 00482 } else { 00483 logger->log_debug("SkillerExecutionThread", "%s wants me to continuously execute '%s'", 00484 m->sender_thread_name(), m->skill_string()); 00485 00486 curss = m->skill_string(); 00487 __continuous_reset = last_was_continuous; // reset if cont exec was in progress 00488 __continuous_run = true; 00489 } 00490 } else { 00491 logger->log_debug("SkillerExecutionThread", "%s tries to exec while not controller", 00492 m->sender_thread_name()); 00493 } 00494 00495 } else if ( __skiller_if->msgq_first_is<SkillerInterface::StopExecMessage>() ) { 00496 SkillerInterface::StopExecMessage *m = __skiller_if->msgq_first<SkillerInterface::StopExecMessage>(); 00497 00498 if ( (m->sender_id() == excl_ctrl) || 00499 (__reader_just_left && (m->sender_id() == __last_exclusive_controller)) ) { 00500 logger->log_debug("SkillerExecutionThread", "Stopping continuous execution"); 00501 if ( __continuous_run ) { 00502 __continuous_run = false; 00503 __continuous_reset = true; 00504 curss = ""; 00505 } 00506 } else { 00507 logger->log_debug("SkillerExecutionThread", "%s tries to stop exec while not controller", 00508 m->sender_thread_name()); 00509 } 00510 } else { 00511 logger->log_warn("SkillerExecutionThread", "Unhandled message of type %s in " 00512 "skiller interface", __skiller_if->msgq_first()->type()); 00513 } 00514 00515 __skiller_if->msgq_pop(); 00516 } 00517 00518 if ( __continuous_run && (curss == "") ) { 00519 curss = __skiller_if->skill_string(); 00520 } 00521 00522 #ifdef SKILLER_TIMETRACKING 00523 __tt->ping_end(__ttc_msgproc); 00524 #endif 00525 00526 if ( __continuous_reset ) { 00527 logger->log_debug("SkillerExecutionThread", "Continuous reset forced"); try { 00528 if (__sksf_pushed) { 00529 __sksf_pushed = false; 00530 __lua->pop(1); // --- 00531 } 00532 __lua->do_string("skillenv.reset_all()"); 00533 } catch (Exception &e) { 00534 logger->log_warn("SkillerExecutionThread", "Caught exception while resetting skills, ignored, output follows"); 00535 logger->log_warn("SkillerExecutionThread", e); 00536 } 00537 00538 __skiller_if->set_status(SkillerInterface::S_INACTIVE); 00539 __skiller_if->set_skill_string(""); 00540 00541 //We're not resetting, because this is information someone might need... 00542 //__skiller_if->set_error(""); 00543 __error_written = false; 00544 __continuous_reset = false; 00545 write_skiller_if = true; 00546 } 00547 00548 if ( write_skiller_if ) __skiller_if->write(); 00549 00550 if ( curss != "" ) { 00551 // We've got something to execute 00552 00553 #ifdef SKILLER_TIMETRACKING 00554 __tt->ping_start(__ttc_luaprep); 00555 #endif 00556 00557 // we're in continuous mode, reset status for this new loop 00558 if ( __continuous_run ) { 00559 // was continuous execution, status has to be cleaned up anyway 00560 //logger->log_debug("SkillerExecutionThread", "Resetting skill status in continuous mode"); 00561 try { 00562 __lua->do_string("skillenv.reset_status()"); 00563 } catch (Exception &e) { 00564 logger->log_warn("SkillerExecutionThread", "Caught exception while resetting status, ignored, output follows"); 00565 logger->log_warn("SkillerExecutionThread", e); 00566 } 00567 } 00568 00569 try { 00570 if (! __sksf_pushed) { 00571 // Stack: 00572 __lua->load_string(curss.c_str()); // sksf (skill string function) 00573 __lua->do_string("return skillenv.gensandbox()"); // sksf, sandbox 00574 __lua->setfenv(); // sksf 00575 __sksf_pushed = true; 00576 } 00577 #ifdef SKILLER_TIMETRACKING 00578 __tt->ping_end(__ttc_luaprep); 00579 __tt->ping_start(__ttc_luaexec); 00580 #endif 00581 __lua->push_value(-1); // sksf sksf 00582 __lua->pcall(); // sksf 00583 00584 } catch (Exception &e) { 00585 logger->log_error("SkillerExecutionThread", e); 00586 __skiller_if->set_error("Skill string execution failed with Lua error, see log"); 00587 __skiller_if->write(); 00588 __continuous_reset = true; 00589 __continuous_run = false; 00590 } 00591 #ifdef SKILLER_TIMETRACKING 00592 __tt->ping_end(__ttc_luaexec); 00593 #endif 00594 00595 if ( ! __continuous_run ) { 00596 // was one-shot execution, cleanup 00597 logger->log_debug("SkillerExecutionThread", "Resetting skills"); 00598 try { 00599 if (__sksf_pushed) { 00600 __sksf_pushed = false; 00601 __lua->pop(1); // --- 00602 } 00603 __lua->do_string("skillenv.reset_all()"); 00604 } catch (Exception &e) { 00605 logger->log_warn("SkillerExecutionThread", "Caught exception while resetting skills, ignored, output follows"); 00606 logger->log_warn("SkillerExecutionThread", e); 00607 } 00608 } 00609 00610 } // end if (curss != "") 00611 00612 #ifdef SKILLER_TIMETRACKING 00613 __tt->ping_start(__ttc_publish); 00614 #endif 00615 publish_skill_status(curss); 00616 publish_skdbg(); 00617 lua_loop_reset(); 00618 00619 __reader_just_left = false; 00620 00621 __lua_ifi->write(); 00622 #ifdef SKILLER_TIMETRACKING 00623 __tt->ping_end(__ttc_publish); 00624 __tt->ping_end(__ttc_total); 00625 if (++__tt_loopcount >= SKILLER_TT_MOD) { 00626 //logger->log_debug("Lua", "Stack size: %i", __lua->stack_size()); 00627 __tt_loopcount = 0; 00628 __tt->print_to_stdout(); 00629 } 00630 #endif 00631 }