(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.
authorDarkvater
Fri, 02 Sep 2005 16:05:59 +0000
changeset 2380 3b26659b4a9a
parent 2379 635e106ff922
child 2381 de9053fe2a2c
(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
network.h
openttd.c
openttd.h
saveload.c
saveload.h
thread.h
unix.c
--- a/functions.h	Thu Sep 01 21:19:48 2005 +0000
+++ b/functions.h	Fri Sep 02 16:05:59 2005 +0000
@@ -276,4 +276,5 @@
 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 */
--- a/network.h	Thu Sep 01 21:19:48 2005 +0000
+++ b/network.h	Fri Sep 02 16:05:59 2005 +0000
@@ -207,7 +207,6 @@
 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 */
 
--- a/openttd.c	Thu Sep 01 21:19:48 2005 +0000
+++ b/openttd.c	Fri Sep 02 16:05:59 2005 +0000
@@ -542,6 +542,38 @@
 	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 @@
 void GameLoop(void)
 {
 	int m;
+	ThreadMsg message;
+
+
+	if ((message = OTTD_PollThreadEvent()) != 0) ProcessSentMessage(message);
 
 	// autosave game?
 	if (_do_autosave) {
--- a/openttd.h	Thu Sep 01 21:19:48 2005 +0000
+++ b/openttd.h	Fri Sep 02 16:05:59 2005 +0000
@@ -543,4 +543,15 @@
 };
 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 */
--- a/saveload.c	Thu Sep 01 21:19:48 2005 +0000
+++ b/saveload.c	Fri Sep 02 16:05:59 2005 +0000
@@ -1255,7 +1255,7 @@
 /** 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 @@
 
 /** 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 @@
 	_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 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 @@
 		_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 @@
 	GetSavegameFormat("memory")->uninit_write(); // clean the memorypool
 	fclose(_sl.fh);
 
-	SaveFileDone();
+	OTTD_SendThreadMessage(MSG_OTTD_SAVETHREAD_DONE);
 	return NULL;
 }
 
@@ -1405,10 +1412,10 @@
 		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 @@
 		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...");
--- a/saveload.h	Thu Sep 01 21:19:48 2005 +0000
+++ b/saveload.h	Fri Sep 02 16:05:59 2005 +0000
@@ -172,4 +172,7 @@
 void SlWriteByte(byte b);
 void SlGlobList(const SaveLoadGlobVarList *desc);
 
+void SaveFileStart(void);
+void SaveFileDone(void);
+void SaveFileError(void);
 #endif /* SAVELOAD_H */
--- a/thread.h	Thu Sep 01 21:19:48 2005 +0000
+++ b/thread.h	Fri Sep 02 16:05:59 2005 +0000
@@ -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 @@
 Thread* OTTDCreateThread(ThreadFunc, void*);
 void*   OTTDJoinThread(Thread*);
 
-#endif
+#endif /* THREAD_H */
--- a/unix.c	Thu Sep 01 21:19:48 2005 +0000
+++ b/unix.c	Fri Sep 02 16:05:59 2005 +0000
@@ -531,8 +531,6 @@
 }
 
 
-#ifdef ENABLE_NETWORK
-
 // multi os compatible sleep function
 
 #ifdef __AMIGA__
@@ -571,6 +569,3 @@
 	}
 	#endif // __AMIGA__
 }
-
-#endif /* ENABLE_NETWORK */
-