From a948fcb6055b5aaf3086913a96e623296d62ef37 Mon Sep 17 00:00:00 2001 From: Darkvater Date: Fri, 2 Sep 2005 16:05:59 +0000 Subject: [PATCH] (svn r2906) Fix some threaded saving problems. Now the thread only interfaces with the main program through a sort of mutex. Communication uses the function OTTD_SendThreadMessage() with the approiate message which is handled in ProcessSentMessage() during the main loop. --- functions.h | 1 + network.h | 1 - openttd.c | 36 ++++++++++++++++++++++++++++++++++++ openttd.h | 11 +++++++++++ saveload.c | 28 +++++++++++++++++----------- saveload.h | 3 +++ thread.h | 8 +------- unix.c | 5 ----- 8 files changed, 69 insertions(+), 24 deletions(-) diff --git a/functions.h b/functions.h index 3c4af9c3c7..073a855172 100644 --- a/functions.h +++ b/functions.h @@ -276,4 +276,5 @@ int ttd_main(int argc, char* argv[]); void DeterminePaths(void); void bubblesort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *)); +void CSleep(int milliseconds); #endif /* FUNCTIONS_H */ diff --git a/network.h b/network.h index 2d231aa7a6..a121af9500 100644 --- a/network.h +++ b/network.h @@ -207,7 +207,6 @@ VARDEF uint8 _network_autoclean_protected; // Unprotect a company after X mont VARDEF uint16 _network_restart_game_date; // If this year is reached, the server automaticly restarts NetworkGameList *NetworkQueryServer(const char* host, unsigned short port, bool game_info); -void CSleep(int milliseconds); #endif /* ENABLE_NETWORK */ diff --git a/openttd.c b/openttd.c index 32837d142e..997950a244 100644 --- a/openttd.c +++ b/openttd.c @@ -542,6 +542,38 @@ int ttd_main(int argc, char* argv[]) return 0; } +/** Mutex so that only one thread can communicate with the main program + * at any given time */ +static ThreadMsg _message = 0; + +static inline void OTTD_ReleaseMutex(void) {_message = 0;} +static inline ThreadMsg OTTD_PollThreadEvent(void) {return _message;} + +/** Called by running thread to execute some action in the main game. + * It will stall as long as the mutex is not freed (handled) by the game */ +void OTTD_SendThreadMessage(ThreadMsg msg) +{ + while (_message != 0) CSleep(10); + + _message = msg; +} + + +/** Handle the user-messages sent to us + * @param message message sent + */ +void ProcessSentMessage(ThreadMsg message) +{ + switch (message) { + case MSG_OTTD_SAVETHREAD_START: SaveFileStart(); break; + case MSG_OTTD_SAVETHREAD_DONE: SaveFileDone(); break; + case MSG_OTTD_SAVETHREAD_ERROR: SaveFileError(); break; + default: NOT_REACHED(); + } + + OTTD_ReleaseMutex(); // release mutex so that other threads, messages can be handled +} + static void ShowScreenshotResult(bool b) { if (b) { @@ -914,6 +946,10 @@ static void HandleKeyScrolling(void) void GameLoop(void) { int m; + ThreadMsg message; + + + if ((message = OTTD_PollThreadEvent()) != 0) ProcessSentMessage(message); // autosave game? if (_do_autosave) { diff --git a/openttd.h b/openttd.h index 5b84d00171..1243cc74a3 100644 --- a/openttd.h +++ b/openttd.h @@ -543,4 +543,15 @@ enum { }; VARDEF byte _no_scroll; +/** To have a concurrently running thread interface with the main program, use + * the OTTD_SendThreadMessage() function. Actions to perform upon the message are handled + * in the ProcessSentMessage() function */ +typedef enum ThreadMsgs { + MSG_OTTD_SAVETHREAD_START = 1, + MSG_OTTD_SAVETHREAD_DONE = 2, + MSG_OTTD_SAVETHREAD_ERROR = 3, +} ThreadMsg; + +void OTTD_SendThreadMessage(ThreadMsg msg); + #endif /* OPENTTD_H */ diff --git a/saveload.c b/saveload.c index cf525e5615..88c8a69c4a 100644 --- a/saveload.c +++ b/saveload.c @@ -1255,7 +1255,7 @@ static inline SaveOrLoadResult AbortSaveLoad(void) /** Update the gui accordingly when starting saving * and set locks on saveload. Also turn off fast-forward cause with that * saving takes Aaaaages */ -static inline void SaveFileStart(void) +void SaveFileStart(void) { _ts.ff_state = _fast_forward; _fast_forward = false; @@ -1267,7 +1267,7 @@ static inline void SaveFileStart(void) /** Update the gui accordingly when saving is done and release locks * on saveload */ -static inline void SaveFileDone(void) +void SaveFileDone(void) { _fast_forward = _ts.ff_state; if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE); @@ -1276,10 +1276,17 @@ static inline void SaveFileDone(void) _ts.saveinprogress = false; } +/** Show a gui message when saving has failed */ +void SaveFileError(void) +{ + ShowErrorMessage(STR_4007_GAME_SAVE_FAILED, STR_NULL, 0, 0); + SaveFileDone(); +} + /** We have written the whole game into memory, _save_pool, now find * and appropiate compressor and start writing to file. */ -static void* SaveFileToDisk(void* arg) +static void* SaveFileToDisk(void *arg) { const SaveLoadFormat *fmt = GetSavegameFormat(_savegame_format); /* XXX - backup _sl.buf cause it is used internally by the writer @@ -1287,6 +1294,8 @@ static void* SaveFileToDisk(void* arg) static byte *tmp = NULL; uint32 hdr[2]; + OTTD_SendThreadMessage(MSG_OTTD_SAVETHREAD_START); + tmp = _sl.buf; /* XXX - Setup setjmp error handler if an error occurs anywhere deep during @@ -1297,9 +1306,7 @@ static void* SaveFileToDisk(void* arg) _sl.excpt_uninit(); ShowInfoF("Save game failed: %s.", _sl.excpt_msg); - ShowErrorMessage(STR_4007_GAME_SAVE_FAILED, STR_NULL, 0, 0); - - SaveFileDone(); + OTTD_SendThreadMessage(MSG_OTTD_SAVETHREAD_ERROR); return NULL; } @@ -1334,7 +1341,7 @@ static void* SaveFileToDisk(void* arg) GetSavegameFormat("memory")->uninit_write(); // clean the memorypool fclose(_sl.fh); - SaveFileDone(); + OTTD_SendThreadMessage(MSG_OTTD_SAVETHREAD_DONE); return NULL; } @@ -1405,10 +1412,10 @@ SaveOrLoadResult SaveOrLoad(const char *filename, int mode) if (mode == SL_LOAD) { ShowInfoF("Load game failed: %s.", _sl.excpt_msg); return SL_REINIT; - } else { - ShowInfoF("Save game failed: %s.", _sl.excpt_msg); - return SL_ERROR; } + + ShowInfoF("Save game failed: %s.", _sl.excpt_msg); + return SL_ERROR; } /* We first initialize here to avoid: "warning: variable `version' might @@ -1434,7 +1441,6 @@ SaveOrLoadResult SaveOrLoad(const char *filename, int mode) SlWriteFill(); // flush the save buffer /* Write to file */ - SaveFileStart(); if (_network_server || (save_thread = OTTDCreateThread(&SaveFileToDisk, NULL)) == NULL) { DEBUG(misc, 1) ("cannot create savegame thread, reverting to single-threaded mode..."); diff --git a/saveload.h b/saveload.h index 78a2b170f4..eb869f4e85 100644 --- a/saveload.h +++ b/saveload.h @@ -172,4 +172,7 @@ void SlSetLength(size_t length); void SlWriteByte(byte b); void SlGlobList(const SaveLoadGlobVarList *desc); +void SaveFileStart(void); +void SaveFileDone(void); +void SaveFileError(void); #endif /* SAVELOAD_H */ diff --git a/thread.h b/thread.h index 73cea8c57a..66e0ceea3b 100644 --- a/thread.h +++ b/thread.h @@ -3,12 +3,6 @@ #ifndef THREAD_H #define THREAD_H -/* - * DO NOT USE THREADS if you don't know what race conditions, mutexes, - * semaphores, atomic operations, etc. are or how to properly handle them. - * Ask somebody who has a clue. - */ - typedef struct Thread Thread; typedef void* (*ThreadFunc)(void*); @@ -16,4 +10,4 @@ typedef void* (*ThreadFunc)(void*); Thread* OTTDCreateThread(ThreadFunc, void*); void* OTTDJoinThread(Thread*); -#endif +#endif /* THREAD_H */ diff --git a/unix.c b/unix.c index 16c79988d7..3496a64522 100644 --- a/unix.c +++ b/unix.c @@ -531,8 +531,6 @@ bool InsertTextBufferClipboard(Textbuf *tb) } -#ifdef ENABLE_NETWORK - // multi os compatible sleep function #ifdef __AMIGA__ @@ -571,6 +569,3 @@ void CSleep(int milliseconds) } #endif // __AMIGA__ } - -#endif /* ENABLE_NETWORK */ -