Fawkes API  Fawkes Development Version
exec_thread.cpp
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 }