Blame 8Kingdoms-1.1.0-tcl-threads.patch

06ba12c
--- 8Kingdoms-1.1.0/common/TCL/tcl_script.cpp	2007-07-22 03:32:50.000000000 +0200
06ba12c
+++ 8Kingdoms-1.1.0.new/common/TCL/tcl_script.cpp	2008-01-08 14:33:59.000000000 +0100
06ba12c
@@ -12,34 +12,96 @@
06ba12c
 #include "common/TCL/tcl_struct.h"
06ba12c
 #include "world/useful.h"
06ba12c
 #include "world/typedefs.h"
06ba12c
+#include "world/rules.h"
06ba12c
 
06ba12c
 using namespace std;
06ba12c
 
06ba12c
+/* We can get called from multiple threads, protected against reentrance
06ba12c
+   through locks. But locks are not enough. Tcl is thread aware and does not
06ba12c
+   allow an interpreter to be called from another thread then it is created.
06ba12c
+   With Tcl-8.4 things work even when violating this Tcl thread model rule, but
06ba12c
+   thats pure luck on our side.
06ba12c
+   
06ba12c
+   However with Tcl-8.5 calling an interpreter from another thread then it is
06ba12c
+   created really no longer works. Under pthread using OS we solve this by
06ba12c
+   using a per thread variable for the interpreter and creating an interpreter
06ba12c
+   for each thread. Under non pthread OS we keep relying on our luck, so
06ba12c
+   Tcl-8.5 may not be used there! */
06ba12c
+
06ba12c
+#ifdef __unix__
06ba12c
+/* interpreter destructor for the per thread interpreter */
06ba12c
+static void interpreter_destructor(void *_interpreter)
06ba12c
+{
06ba12c
+    Tcl_DeleteInterp((Tcl_Interp *)_interpreter);
06ba12c
+}
06ba12c
+#endif
06ba12c
+
06ba12c
 TTCL_Interpreter::TTCL_Interpreter()
06ba12c
 {
06ba12c
-    _interpreter =  Tcl_CreateInterp();
06ba12c
+#ifdef __unix__
06ba12c
+    pthread_key_create(&_interpreter_key, interpreter_destructor);
06ba12c
+    _rules = NULL; 
06ba12c
+#else
06ba12c
+    _interpreter = Tcl_CreateInterp();
06ba12c
+#endif
06ba12c
 }
06ba12c
 
06ba12c
 TTCL_Interpreter::~TTCL_Interpreter()
06ba12c
 {
06ba12c
+#ifdef __unix__
06ba12c
+    pthread_key_delete(_interpreter_key);
06ba12c
+#else
06ba12c
     Tcl_DeleteInterp(_interpreter);
06ba12c
     //KMemFree(_interpreter);
06ba12c
+#endif
06ba12c
+}
06ba12c
+
06ba12c
+Tcl_Interp *TTCL_Interpreter::getInterpreter()
06ba12c
+{
06ba12c
+#ifdef __unix__
06ba12c
+    Tcl_Interp *_interpreter =
06ba12c
+        (Tcl_Interp *)pthread_getspecific(_interpreter_key);
06ba12c
+    
06ba12c
+    if (!_interpreter)
06ba12c
+    {
06ba12c
+        _interpreter = Tcl_CreateInterp();
06ba12c
+        pthread_setspecific(_interpreter_key, _interpreter);
06ba12c
+        
06ba12c
+        if (_rules)
06ba12c
+        {
06ba12c
+            _rules->writeToTCL(*this);
06ba12c
+            Tcl_CreateCommand(_interpreter, "KSendMessage", _TCLSendMessageProc,
06ba12c
+                              NULL, NULL);
06ba12c
+            TTCL_Script script(this);
06ba12c
+            script.loadStruct(_init_script);
06ba12c
+            script.run();
06ba12c
+        }
06ba12c
+    }
06ba12c
+#endif
06ba12c
+    return _interpreter;
06ba12c
 }
06ba12c
 
06ba12c
-void TTCL_Interpreter::init(TCL_SCRIPT * init_script)
06ba12c
+void TTCL_Interpreter::init(World::TRules * rules,
06ba12c
+    Tcl_CmdProc * TCLSendMessageProc, TCL_SCRIPT * init_script)
06ba12c
 {
06ba12c
+#ifdef __unix__
06ba12c
+    _rules = rules;
06ba12c
+    _TCLSendMessageProc = TCLSendMessageProc;
06ba12c
+    _init_script = init_script;
06ba12c
+#else
06ba12c
+    rules->writeToTCL(*this);
06ba12c
+    Tcl_CreateCommand(getInterpreter(), "KSendMessage", TCLSendMessageProc,
06ba12c
+                      NULL, NULL);
06ba12c
     TTCL_Script script(this);
06ba12c
     script.loadStruct(init_script);
06ba12c
     script.run();
06ba12c
+#endif
06ba12c
 }
06ba12c
 
06ba12c
 bool TTCL_Interpreter::setVar(const char * name, const TCL_VAR_TYPE type, void * value)
06ba12c
 {
06ba12c
-    if (_interpreter == NULL)
06ba12c
-        THROW(E_8K_TCL_Error, "TCL interpreter has not been initialized properly");
06ba12c
-
06ba12c
     // odstraneni predchoziho vyskytu promenne
06ba12c
-    Tcl_UnsetVar(_interpreter, name, 0);
06ba12c
+    Tcl_UnsetVar(getInterpreter(), name, 0);
06ba12c
     
06ba12c
     // nastaveni nove hodnoty
06ba12c
     bool result;
06ba12c
@@ -48,7 +110,7 @@
06ba12c
     switch (type) {
06ba12c
         case TVT_STRING:
06ba12c
         // STRING
06ba12c
-            result = (Tcl_SetVar(_interpreter, name, (const char *)value, 0) != NULL);
06ba12c
+            result = (Tcl_SetVar(getInterpreter(), name, (const char *)value, 0) != NULL);
06ba12c
             break;
06ba12c
         case TVT_INT:
06ba12c
         // INT
06ba12c
@@ -56,7 +118,7 @@
06ba12c
             snprintf(s, 255, "%d", *(int *)value);
06ba12c
 
06ba12c
             // ulozim cislo do TCL
06ba12c
-            result = (Tcl_SetVar(_interpreter, name, s, 0) != NULL);
06ba12c
+            result = (Tcl_SetVar(getInterpreter(), name, s, 0) != NULL);
06ba12c
             break;
06ba12c
         case TVT_FLOAT:
06ba12c
         // FLOAT
06ba12c
@@ -64,7 +126,7 @@
06ba12c
             snprintf(s, 255, "%f", *(float *)value);
06ba12c
 
06ba12c
             // ulozim cislo do TCL
06ba12c
-            result = (Tcl_SetVar(_interpreter, name, s, 0) != NULL);
06ba12c
+            result = (Tcl_SetVar(getInterpreter(), name, s, 0) != NULL);
06ba12c
             break;
06ba12c
         case TVT_FLOAT_LIST:
06ba12c
             {
06ba12c
@@ -153,11 +215,8 @@
06ba12c
 
06ba12c
 bool TTCL_Interpreter::getVar(const char * name, const TCL_VAR_TYPE type, void * value)
06ba12c
 {
06ba12c
-    if (_interpreter == NULL)
06ba12c
-        THROW(E_8K_TCL_Error, "TCL interpreter has not been initialized properly");
06ba12c
-
06ba12c
     // nacteni objektu z TCL
06ba12c
-    Tcl_Obj * tcl_obj = Tcl_GetVar2Ex(_interpreter, name, NULL, TCL_LEAVE_ERR_MSG);
06ba12c
+    Tcl_Obj * tcl_obj = Tcl_GetVar2Ex(getInterpreter(), name, NULL, TCL_LEAVE_ERR_MSG);
06ba12c
     
06ba12c
     // promennou se nepodarilo nacist (a pritom to nebylo asociativni pole)
06ba12c
     if ((tcl_obj == NULL) && (type != TVT_INT_ARRAY) && (type != TVT_FLOAT_ARRAY) && (type != TVT_STRING_ARRAY)) {        
06ba12c
@@ -173,14 +232,14 @@
06ba12c
                 return true;
06ba12c
             break;
06ba12c
         case TVT_INT:
06ba12c
-            if (Tcl_GetIntFromObj(_interpreter, tcl_obj, (int *)value) == TCL_OK)
06ba12c
+            if (Tcl_GetIntFromObj(getInterpreter(), tcl_obj, (int *)value) == TCL_OK)
06ba12c
                 return true;
06ba12c
             else
06ba12c
                 return false;
06ba12c
             break;
06ba12c
         case TVT_FLOAT:
06ba12c
             double _value;
06ba12c
-            if (Tcl_GetDoubleFromObj(_interpreter, tcl_obj, &_value) == TCL_OK) {
06ba12c
+            if (Tcl_GetDoubleFromObj(getInterpreter(), tcl_obj, &_value) == TCL_OK) {
06ba12c
                 // pretypovani na float
06ba12c
                 *(float *)value = (float)_value;
06ba12c
                 return true;
06ba12c
@@ -415,7 +474,7 @@
06ba12c
     char s[MAX_STRLEN];
06ba12c
 
06ba12c
     snprintf(s, MAX_STRLEN, "%d", value);
06ba12c
-    return (Tcl_SetVar(_interpreter, name, s, 0) != NULL);
06ba12c
+    return (Tcl_SetVar(getInterpreter(), name, s, 0) != NULL);
06ba12c
 }
06ba12c
 
06ba12c
 bool TTCL_Interpreter::setConstDouble(const char * name, const double value)
06ba12c
@@ -423,29 +482,21 @@
06ba12c
     char s[MAX_STRLEN];
06ba12c
 
06ba12c
     snprintf(s, MAX_STRLEN, "%.2f", value);
06ba12c
-    return (Tcl_SetVar(_interpreter, name, s, 0) != NULL);
06ba12c
+    return (Tcl_SetVar(getInterpreter(), name, s, 0) != NULL);
06ba12c
 }
06ba12c
 
06ba12c
 int TTCL_Interpreter::eval(const char * code)
06ba12c
 {
06ba12c
-    if (_interpreter == NULL)
06ba12c
-        THROW(E_8K_TCL_Error, "TCL interpreter has not been initialized properly");
06ba12c
-
06ba12c
-    int result =  Tcl_Eval(_interpreter, code);
06ba12c
+    int result =  Tcl_Eval(getInterpreter(), code);
06ba12c
     if (result == TCL_ERROR) {
06ba12c
-        THROW(E_8K_TCL_Error, Tcl_GetStringResult(_interpreter));
06ba12c
+        THROW(E_8K_TCL_Error, Tcl_GetStringResult(getInterpreter()));
06ba12c
     }
06ba12c
     return result;
06ba12c
 }
06ba12c
 
06ba12c
 const char * TTCL_Interpreter::getError()
06ba12c
 {
06ba12c
-    return Tcl_GetStringResult(_interpreter);
06ba12c
-}
06ba12c
-
06ba12c
-Tcl_Command TTCL_Interpreter::createCommand(const char * tclName, Tcl_CmdProc * cName, ClientData clientData, Tcl_CmdDeleteProc * deleteProc)
06ba12c
-{
06ba12c
-    return Tcl_CreateCommand(_interpreter, tclName, cName, clientData, deleteProc);
06ba12c
+    return Tcl_GetStringResult(getInterpreter());
06ba12c
 }
06ba12c
 
06ba12c
 void TTCL_Interpreter::setResult(TCL_VAR_TYPE type, void * value)
06ba12c
@@ -455,21 +506,21 @@
06ba12c
     switch (type) {
06ba12c
         case TVT_STRING:
06ba12c
         // STRING
06ba12c
-            Tcl_SetResult(_interpreter, (char *)value, NULL);
06ba12c
+            Tcl_SetResult(getInterpreter(), (char *)value, NULL);
06ba12c
             break;
06ba12c
         case TVT_INT:
06ba12c
         // INT
06ba12c
             // prevedu cislo na string        
06ba12c
             snprintf(s, MAX_STRLEN, "%d", *(int *)value);
06ba12c
 
06ba12c
-            Tcl_SetResult(_interpreter, s, NULL);
06ba12c
+            Tcl_SetResult(getInterpreter(), s, NULL);
06ba12c
             break;
06ba12c
         case TVT_FLOAT:
06ba12c
         // FLOAT
06ba12c
             // prevedu cislo na string
06ba12c
             snprintf(s, MAX_STRLEN, "%f", *(float *)value);
06ba12c
 
06ba12c
-            Tcl_SetResult(_interpreter, s, NULL);
06ba12c
+            Tcl_SetResult(getInterpreter(), s, NULL);
06ba12c
             break;
06ba12c
         default:
06ba12c
             THROW(E_8K_TCL_UnknownType, "");
06ba12c
--- 8Kingdoms-1.1.0/common/TCL/tcl_script.h	2007-07-22 03:32:50.000000000 +0200
06ba12c
+++ 8Kingdoms-1.1.0.new/common/TCL/tcl_script.h	2008-01-08 14:30:55.000000000 +0100
06ba12c
@@ -9,6 +9,9 @@
06ba12c
 
06ba12c
 #include <map>
06ba12c
 #include <tcl.h>
06ba12c
+#ifdef __unix__
06ba12c
+#include <pthread.h>
06ba12c
+#endif
06ba12c
 #include "common/TCL/tcl_var.h"
06ba12c
 
06ba12c
 /// pocet desetinnych mist, pouzitych pri konverzi cisel do TCL
06ba12c
@@ -172,6 +175,11 @@
06ba12c
     char * code;
06ba12c
 };
06ba12c
 
06ba12c
+namespace World
06ba12c
+{
06ba12c
+    class TRules;
06ba12c
+};
06ba12c
+
06ba12c
 /** Interpret skriptu jazyka TCL
06ba12c
     Objektovy "obal" puvodnich TCL struktur. 
06ba12c
     
06ba12c
@@ -184,16 +192,26 @@
06ba12c
 class TTCL_Interpreter
06ba12c
 {
06ba12c
 private:
06ba12c
+#ifdef __unix__
06ba12c
+    pthread_key_t _interpreter_key;
06ba12c
+    TCL_SCRIPT * _init_script;
06ba12c
+    Tcl_CmdProc * _TCLSendMessageProc;
06ba12c
+    World::TRules * _rules;
06ba12c
+#else
06ba12c
     Tcl_Interp * _interpreter;
06ba12c
+#endif
06ba12c
+    Tcl_Interp *getInterpreter();
06ba12c
+
06ba12c
 public:
06ba12c
     TTCL_Interpreter();
06ba12c
     ~TTCL_Interpreter();
06ba12c
-
06ba12c
+    
06ba12c
     /** Inicializace prostredi TCL interpreteru.
06ba12c
         Nastaveni globalnich promennych, inkluze knihoven a definice spolecnych
06ba12c
         funkci.
06ba12c
         */
06ba12c
-    void init(TCL_SCRIPT * init_script);
06ba12c
+    void init(World::TRules * rules, Tcl_CmdProc * TCLSendMessageProc,
06ba12c
+              TCL_SCRIPT * init_script);
06ba12c
 
06ba12c
     /** Ulozeni promenne do prostredi TCL interpretu 
06ba12c
         @param name jmeno promenne
06ba12c
@@ -256,15 +274,6 @@
06ba12c
     */
06ba12c
     const char * getError();
06ba12c
 
06ba12c
-    /** Nastaveni uzivatelske funkce.
06ba12c
-        Umozni asociovat volani funkce z TCL s funkci v C
06ba12c
-        @param  tclName     jmeno funkce v TCL, jejiz volani chceme obsluhovat sami
06ba12c
-        @param  cName       funkce z C, ktera bude realizovat telo funkce z TCL
06ba12c
-        @param  clientData  arbitrary one-word value to pass to proc and deleteProc. 
06ba12c
-        @param  deleteProc  procedure to call before cmdName is deleted from the interpreter; allows for command-specific cleanup. If NULL, then no procedure is called before the command is deleted.
06ba12c
-        */
06ba12c
-    Tcl_Command createCommand(const char * tclName, Tcl_CmdProc * cName, ClientData clientData = NULL, Tcl_CmdDeleteProc * deleteProc = NULL);
06ba12c
-    
06ba12c
     /// Nastaveni navratove hodnoty funkce 
06ba12c
     void setResult(TCL_VAR_TYPE type, void * value);
06ba12c
 };
06ba12c
--- 8Kingdoms-1.1.0/world/world_client.cpp	2008-01-08 14:39:20.000000000 +0100
06ba12c
+++ 8Kingdoms-1.1.0.new/world/world_client.cpp	2008-01-08 14:37:05.000000000 +0100
06ba12c
@@ -107,13 +107,7 @@
06ba12c
     world->init();
06ba12c
 
06ba12c
     // inicializace negine
06ba12c
-    engine->init(world);
06ba12c
-    
06ba12c
-    // registrace zprav z TCL
06ba12c
-    engine->interpreter.createCommand("KSendMessage", (Tcl_CmdProc *)World::WorldClient_SendMessage);
06ba12c
-
06ba12c
-    // inkluze knihoven pro TCL (spusteni inicializacniho skriptu)
06ba12c
-    engine->interpreter.init(world->rules->scripts[TS_INIT]);
06ba12c
+    engine->init(world, (Tcl_CmdProc *)World::WorldClient_SendMessage);
06ba12c
   }
06ba12c
 
06ba12c
   // svet je pripraven
06ba12c
--- 8Kingdoms-1.1.0/world/world_engine.cpp	2008-01-08 14:39:21.000000000 +0100
06ba12c
+++ 8Kingdoms-1.1.0.new/world/world_engine.cpp	2008-01-08 14:38:37.000000000 +0100
06ba12c
@@ -30,11 +29,10 @@
06ba12c
  
06ba12c
 }
06ba12c
 
06ba12c
-void TWorldEngine::init(TWorld * world)
06ba12c
+void TWorldEngine::init(TWorld * world, Tcl_CmdProc *TCLSendMessageProc)
06ba12c
 {
06ba12c
-  // zapis pravidel do prostredi TCL interpreteru
06ba12c
-  if (world->rules)
06ba12c
-    world->rules->writeToTCL(interpreter);
06ba12c
+  interpreter.init(world->rules, TCLSendMessageProc,
06ba12c
+                   world->rules->scripts[TS_INIT]);
06ba12c
 }
06ba12c
 
06ba12c
 int TWorldEngine::lock()
06ba12c
--- 8Kingdoms-1.1.0/world/world_engine.h	2007-07-22 03:33:12.000000000 +0200
06ba12c
+++ 8Kingdoms-1.1.0.new/world/world_engine.h	2008-01-08 13:44:32.000000000 +0100
06ba12c
@@ -45,7 +45,7 @@
06ba12c
         ~TWorldEngine();
06ba12c
 
06ba12c
         /** Inicializace */
06ba12c
-        void init(TWorld * world);
06ba12c
+        void init(TWorld * world, Tcl_CmdProc *TCLSendMessageProc);
06ba12c
 
06ba12c
         /// Zamceni mutexu
06ba12c
         int lock();
06ba12c
--- 8Kingdoms-1.1.0/world/world_server.cpp	2008-01-08 14:39:20.000000000 +0100
06ba12c
+++ 8Kingdoms-1.1.0.new/world/world_server.cpp	2008-01-08 14:36:44.000000000 +0100
06ba12c
@@ -87,13 +87,7 @@
06ba12c
   world.init();
06ba12c
 
06ba12c
   // inicializace world_engine
06ba12c
-  engine.init(&world);
06ba12c
-
06ba12c
-  // registrace zprav z TCL
06ba12c
-  engine.interpreter.createCommand("KSendMessage", (Tcl_CmdProc *)WorldServer_SendMessage);
06ba12c
-
06ba12c
-  // inkluze knihoven pro TCL (spusteni inicializacniho skriptu)
06ba12c
-  engine.interpreter.init(world.rules->scripts[TS_INIT]);
06ba12c
+  engine.init(&world, (Tcl_CmdProc *)WorldServer_SendMessage);
06ba12c
 }
06ba12c
 
06ba12c
 void TWorldServer::shutdown()
06ba12c
--- 8Kingdoms-1.1.0/8Kingdoms.cpp~	2008-01-08 20:31:01.000000000 +0100
06ba12c
+++ 8Kingdoms-1.1.0/8Kingdoms.cpp	2008-01-08 20:31:01.000000000 +0100
06ba12c
@@ -25,6 +25,7 @@
06ba12c
 
06ba12c
 //rozhrani
06ba12c
 #include <sstream>
06ba12c
+#include <tcl.h>
06ba12c
 
06ba12c
 #include "common/Interface.h"
06ba12c
 #include "common/Msg.h"
06ba12c
@@ -470,6 +471,8 @@ void print_help(){
06ba12c
 */
06ba12c
 int main(int argc, char** argv){
06ba12c
 
06ba12c
+  Tcl_FindExecutable(argv[0]);
06ba12c
+  
06ba12c
   try{
06ba12c
     //parametry z prikazoveho radku
06ba12c
     TCommandLine::parseCommandLine(argc, argv);