diff --git a/8Kingdoms-1.1.0-tcl-threads.patch b/8Kingdoms-1.1.0-tcl-threads.patch new file mode 100644 index 0000000..c5fcc1b --- /dev/null +++ b/8Kingdoms-1.1.0-tcl-threads.patch @@ -0,0 +1,380 @@ +--- 8Kingdoms-1.1.0/common/TCL/tcl_script.cpp 2007-07-22 03:32:50.000000000 +0200 ++++ 8Kingdoms-1.1.0.new/common/TCL/tcl_script.cpp 2008-01-08 14:33:59.000000000 +0100 +@@ -12,34 +12,96 @@ + #include "common/TCL/tcl_struct.h" + #include "world/useful.h" + #include "world/typedefs.h" ++#include "world/rules.h" + + using namespace std; + ++/* We can get called from multiple threads, protected against reentrance ++ through locks. But locks are not enough. Tcl is thread aware and does not ++ allow an interpreter to be called from another thread then it is created. ++ With Tcl-8.4 things work even when violating this Tcl thread model rule, but ++ thats pure luck on our side. ++ ++ However with Tcl-8.5 calling an interpreter from another thread then it is ++ created really no longer works. Under pthread using OS we solve this by ++ using a per thread variable for the interpreter and creating an interpreter ++ for each thread. Under non pthread OS we keep relying on our luck, so ++ Tcl-8.5 may not be used there! */ ++ ++#ifdef __unix__ ++/* interpreter destructor for the per thread interpreter */ ++static void interpreter_destructor(void *_interpreter) ++{ ++ Tcl_DeleteInterp((Tcl_Interp *)_interpreter); ++} ++#endif ++ + TTCL_Interpreter::TTCL_Interpreter() + { +- _interpreter = Tcl_CreateInterp(); ++#ifdef __unix__ ++ pthread_key_create(&_interpreter_key, interpreter_destructor); ++ _rules = NULL; ++#else ++ _interpreter = Tcl_CreateInterp(); ++#endif + } + + TTCL_Interpreter::~TTCL_Interpreter() + { ++#ifdef __unix__ ++ pthread_key_delete(_interpreter_key); ++#else + Tcl_DeleteInterp(_interpreter); + //KMemFree(_interpreter); ++#endif ++} ++ ++Tcl_Interp *TTCL_Interpreter::getInterpreter() ++{ ++#ifdef __unix__ ++ Tcl_Interp *_interpreter = ++ (Tcl_Interp *)pthread_getspecific(_interpreter_key); ++ ++ if (!_interpreter) ++ { ++ _interpreter = Tcl_CreateInterp(); ++ pthread_setspecific(_interpreter_key, _interpreter); ++ ++ if (_rules) ++ { ++ _rules->writeToTCL(*this); ++ Tcl_CreateCommand(_interpreter, "KSendMessage", _TCLSendMessageProc, ++ NULL, NULL); ++ TTCL_Script script(this); ++ script.loadStruct(_init_script); ++ script.run(); ++ } ++ } ++#endif ++ return _interpreter; + } + +-void TTCL_Interpreter::init(TCL_SCRIPT * init_script) ++void TTCL_Interpreter::init(World::TRules * rules, ++ Tcl_CmdProc * TCLSendMessageProc, TCL_SCRIPT * init_script) + { ++#ifdef __unix__ ++ _rules = rules; ++ _TCLSendMessageProc = TCLSendMessageProc; ++ _init_script = init_script; ++#else ++ rules->writeToTCL(*this); ++ Tcl_CreateCommand(getInterpreter(), "KSendMessage", TCLSendMessageProc, ++ NULL, NULL); + TTCL_Script script(this); + script.loadStruct(init_script); + script.run(); ++#endif + } + + bool TTCL_Interpreter::setVar(const char * name, const TCL_VAR_TYPE type, void * value) + { +- if (_interpreter == NULL) +- THROW(E_8K_TCL_Error, "TCL interpreter has not been initialized properly"); +- + // odstraneni predchoziho vyskytu promenne +- Tcl_UnsetVar(_interpreter, name, 0); ++ Tcl_UnsetVar(getInterpreter(), name, 0); + + // nastaveni nove hodnoty + bool result; +@@ -48,7 +110,7 @@ + switch (type) { + case TVT_STRING: + // STRING +- result = (Tcl_SetVar(_interpreter, name, (const char *)value, 0) != NULL); ++ result = (Tcl_SetVar(getInterpreter(), name, (const char *)value, 0) != NULL); + break; + case TVT_INT: + // INT +@@ -56,7 +118,7 @@ + snprintf(s, 255, "%d", *(int *)value); + + // ulozim cislo do TCL +- result = (Tcl_SetVar(_interpreter, name, s, 0) != NULL); ++ result = (Tcl_SetVar(getInterpreter(), name, s, 0) != NULL); + break; + case TVT_FLOAT: + // FLOAT +@@ -64,7 +126,7 @@ + snprintf(s, 255, "%f", *(float *)value); + + // ulozim cislo do TCL +- result = (Tcl_SetVar(_interpreter, name, s, 0) != NULL); ++ result = (Tcl_SetVar(getInterpreter(), name, s, 0) != NULL); + break; + case TVT_FLOAT_LIST: + { +@@ -153,11 +215,8 @@ + + bool TTCL_Interpreter::getVar(const char * name, const TCL_VAR_TYPE type, void * value) + { +- if (_interpreter == NULL) +- THROW(E_8K_TCL_Error, "TCL interpreter has not been initialized properly"); +- + // nacteni objektu z TCL +- Tcl_Obj * tcl_obj = Tcl_GetVar2Ex(_interpreter, name, NULL, TCL_LEAVE_ERR_MSG); ++ Tcl_Obj * tcl_obj = Tcl_GetVar2Ex(getInterpreter(), name, NULL, TCL_LEAVE_ERR_MSG); + + // promennou se nepodarilo nacist (a pritom to nebylo asociativni pole) + if ((tcl_obj == NULL) && (type != TVT_INT_ARRAY) && (type != TVT_FLOAT_ARRAY) && (type != TVT_STRING_ARRAY)) { +@@ -173,14 +232,14 @@ + return true; + break; + case TVT_INT: +- if (Tcl_GetIntFromObj(_interpreter, tcl_obj, (int *)value) == TCL_OK) ++ if (Tcl_GetIntFromObj(getInterpreter(), tcl_obj, (int *)value) == TCL_OK) + return true; + else + return false; + break; + case TVT_FLOAT: + double _value; +- if (Tcl_GetDoubleFromObj(_interpreter, tcl_obj, &_value) == TCL_OK) { ++ if (Tcl_GetDoubleFromObj(getInterpreter(), tcl_obj, &_value) == TCL_OK) { + // pretypovani na float + *(float *)value = (float)_value; + return true; +@@ -415,7 +474,7 @@ + char s[MAX_STRLEN]; + + snprintf(s, MAX_STRLEN, "%d", value); +- return (Tcl_SetVar(_interpreter, name, s, 0) != NULL); ++ return (Tcl_SetVar(getInterpreter(), name, s, 0) != NULL); + } + + bool TTCL_Interpreter::setConstDouble(const char * name, const double value) +@@ -423,29 +482,21 @@ + char s[MAX_STRLEN]; + + snprintf(s, MAX_STRLEN, "%.2f", value); +- return (Tcl_SetVar(_interpreter, name, s, 0) != NULL); ++ return (Tcl_SetVar(getInterpreter(), name, s, 0) != NULL); + } + + int TTCL_Interpreter::eval(const char * code) + { +- if (_interpreter == NULL) +- THROW(E_8K_TCL_Error, "TCL interpreter has not been initialized properly"); +- +- int result = Tcl_Eval(_interpreter, code); ++ int result = Tcl_Eval(getInterpreter(), code); + if (result == TCL_ERROR) { +- THROW(E_8K_TCL_Error, Tcl_GetStringResult(_interpreter)); ++ THROW(E_8K_TCL_Error, Tcl_GetStringResult(getInterpreter())); + } + return result; + } + + const char * TTCL_Interpreter::getError() + { +- return Tcl_GetStringResult(_interpreter); +-} +- +-Tcl_Command TTCL_Interpreter::createCommand(const char * tclName, Tcl_CmdProc * cName, ClientData clientData, Tcl_CmdDeleteProc * deleteProc) +-{ +- return Tcl_CreateCommand(_interpreter, tclName, cName, clientData, deleteProc); ++ return Tcl_GetStringResult(getInterpreter()); + } + + void TTCL_Interpreter::setResult(TCL_VAR_TYPE type, void * value) +@@ -455,21 +506,21 @@ + switch (type) { + case TVT_STRING: + // STRING +- Tcl_SetResult(_interpreter, (char *)value, NULL); ++ Tcl_SetResult(getInterpreter(), (char *)value, NULL); + break; + case TVT_INT: + // INT + // prevedu cislo na string + snprintf(s, MAX_STRLEN, "%d", *(int *)value); + +- Tcl_SetResult(_interpreter, s, NULL); ++ Tcl_SetResult(getInterpreter(), s, NULL); + break; + case TVT_FLOAT: + // FLOAT + // prevedu cislo na string + snprintf(s, MAX_STRLEN, "%f", *(float *)value); + +- Tcl_SetResult(_interpreter, s, NULL); ++ Tcl_SetResult(getInterpreter(), s, NULL); + break; + default: + THROW(E_8K_TCL_UnknownType, ""); +--- 8Kingdoms-1.1.0/common/TCL/tcl_script.h 2007-07-22 03:32:50.000000000 +0200 ++++ 8Kingdoms-1.1.0.new/common/TCL/tcl_script.h 2008-01-08 14:30:55.000000000 +0100 +@@ -9,6 +9,9 @@ + + #include + #include ++#ifdef __unix__ ++#include ++#endif + #include "common/TCL/tcl_var.h" + + /// pocet desetinnych mist, pouzitych pri konverzi cisel do TCL +@@ -172,6 +175,11 @@ + char * code; + }; + ++namespace World ++{ ++ class TRules; ++}; ++ + /** Interpret skriptu jazyka TCL + Objektovy "obal" puvodnich TCL struktur. + +@@ -184,16 +192,26 @@ + class TTCL_Interpreter + { + private: ++#ifdef __unix__ ++ pthread_key_t _interpreter_key; ++ TCL_SCRIPT * _init_script; ++ Tcl_CmdProc * _TCLSendMessageProc; ++ World::TRules * _rules; ++#else + Tcl_Interp * _interpreter; ++#endif ++ Tcl_Interp *getInterpreter(); ++ + public: + TTCL_Interpreter(); + ~TTCL_Interpreter(); +- ++ + /** Inicializace prostredi TCL interpreteru. + Nastaveni globalnich promennych, inkluze knihoven a definice spolecnych + funkci. + */ +- void init(TCL_SCRIPT * init_script); ++ void init(World::TRules * rules, Tcl_CmdProc * TCLSendMessageProc, ++ TCL_SCRIPT * init_script); + + /** Ulozeni promenne do prostredi TCL interpretu + @param name jmeno promenne +@@ -256,15 +274,6 @@ + */ + const char * getError(); + +- /** Nastaveni uzivatelske funkce. +- Umozni asociovat volani funkce z TCL s funkci v C +- @param tclName jmeno funkce v TCL, jejiz volani chceme obsluhovat sami +- @param cName funkce z C, ktera bude realizovat telo funkce z TCL +- @param clientData arbitrary one-word value to pass to proc and deleteProc. +- @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. +- */ +- Tcl_Command createCommand(const char * tclName, Tcl_CmdProc * cName, ClientData clientData = NULL, Tcl_CmdDeleteProc * deleteProc = NULL); +- + /// Nastaveni navratove hodnoty funkce + void setResult(TCL_VAR_TYPE type, void * value); + }; +--- 8Kingdoms-1.1.0/world/world_client.cpp 2008-01-08 14:39:20.000000000 +0100 ++++ 8Kingdoms-1.1.0.new/world/world_client.cpp 2008-01-08 14:37:05.000000000 +0100 +@@ -107,13 +107,7 @@ + world->init(); + + // inicializace negine +- engine->init(world); +- +- // registrace zprav z TCL +- engine->interpreter.createCommand("KSendMessage", (Tcl_CmdProc *)World::WorldClient_SendMessage); +- +- // inkluze knihoven pro TCL (spusteni inicializacniho skriptu) +- engine->interpreter.init(world->rules->scripts[TS_INIT]); ++ engine->init(world, (Tcl_CmdProc *)World::WorldClient_SendMessage); + } + + // svet je pripraven +--- 8Kingdoms-1.1.0/world/world_engine.cpp 2008-01-08 14:39:21.000000000 +0100 ++++ 8Kingdoms-1.1.0.new/world/world_engine.cpp 2008-01-08 14:38:37.000000000 +0100 +@@ -30,11 +29,10 @@ + + } + +-void TWorldEngine::init(TWorld * world) ++void TWorldEngine::init(TWorld * world, Tcl_CmdProc *TCLSendMessageProc) + { +- // zapis pravidel do prostredi TCL interpreteru +- if (world->rules) +- world->rules->writeToTCL(interpreter); ++ interpreter.init(world->rules, TCLSendMessageProc, ++ world->rules->scripts[TS_INIT]); + } + + int TWorldEngine::lock() +--- 8Kingdoms-1.1.0/world/world_engine.h 2007-07-22 03:33:12.000000000 +0200 ++++ 8Kingdoms-1.1.0.new/world/world_engine.h 2008-01-08 13:44:32.000000000 +0100 +@@ -45,7 +45,7 @@ + ~TWorldEngine(); + + /** Inicializace */ +- void init(TWorld * world); ++ void init(TWorld * world, Tcl_CmdProc *TCLSendMessageProc); + + /// Zamceni mutexu + int lock(); +--- 8Kingdoms-1.1.0/world/world_server.cpp 2008-01-08 14:39:20.000000000 +0100 ++++ 8Kingdoms-1.1.0.new/world/world_server.cpp 2008-01-08 14:36:44.000000000 +0100 +@@ -87,13 +87,7 @@ + world.init(); + + // inicializace world_engine +- engine.init(&world); +- +- // registrace zprav z TCL +- engine.interpreter.createCommand("KSendMessage", (Tcl_CmdProc *)WorldServer_SendMessage); +- +- // inkluze knihoven pro TCL (spusteni inicializacniho skriptu) +- engine.interpreter.init(world.rules->scripts[TS_INIT]); ++ engine.init(&world, (Tcl_CmdProc *)WorldServer_SendMessage); + } + + void TWorldServer::shutdown() +--- 8Kingdoms-1.1.0/8Kingdoms.cpp~ 2008-01-08 20:31:01.000000000 +0100 ++++ 8Kingdoms-1.1.0/8Kingdoms.cpp 2008-01-08 20:31:01.000000000 +0100 +@@ -25,6 +25,7 @@ + + //rozhrani + #include ++#include + + #include "common/Interface.h" + #include "common/Msg.h" +@@ -470,6 +471,8 @@ void print_help(){ + */ + int main(int argc, char** argv){ + ++ Tcl_FindExecutable(argv[0]); ++ + try{ + //parametry z prikazoveho radku + TCommandLine::parseCommandLine(argc, argv); diff --git a/8Kingdoms.spec b/8Kingdoms.spec index 1bd292d..1c7f211 100644 --- a/8Kingdoms.spec +++ b/8Kingdoms.spec @@ -5,7 +5,7 @@ Name: 8Kingdoms Version: 1.1.0 -Release: 4%{?dist} +Release: 5%{?dist} Summary: 8 Kingdoms is a 3D turn-based fantasy strategic game Group: Amusements/Games License: GPL+ @@ -27,6 +27,7 @@ Patch2: 8Kingdoms-1.1.0-crash.patch Patch3: 8Kingdoms-1.1.0-gcc43.patch Patch4: 8Kingdoms-1.1.0-divide-by-zero.patch Patch5: 8Kingdoms-1.1.0-cvs-fixes.patch +Patch6: 8Kingdoms-1.1.0-tcl-threads.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: expat-devel SDL_mixer-devel tcl-devel desktop-file-utils Requires: hicolor-icon-theme opengl-games-utils @@ -54,6 +55,7 @@ random map generator is included for comfortable map editing. %patch3 -p1 %patch4 -p1 %patch5 -p1 +%patch6 -p1 cp -a %{SOURCE1} external chmod -x doc/gui/gui_img1.png # configure won't recognize --datadir ... @@ -108,6 +110,10 @@ fi %changelog +* Tue Jan 8 2008 Hans de Goede 1.1.0-5 +- Don't call a TclInterpreter from other threads then its created in, this + violates Tcl's thread model, this fixes running with Tcl-8.5 + * Fri Jan 4 2008 Hans de Goede 1.1.0-4 - Fix divide by zero abort (bz 427485) - Backport various fixes from CVS