(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.

This commit is contained in:
Darkvater 2005-09-02 16:05:59 +00:00
parent acf442102a
commit a948fcb605
8 changed files with 69 additions and 24 deletions

View File

@ -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 */

View File

@ -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 */

View File

@ -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) {

View File

@ -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 */

View File

@ -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,6 +1276,13 @@ 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.
*/
@ -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,11 +1412,11 @@ 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;
}
}
/* We first initialize here to avoid: "warning: variable `version' might
* be clobbered by `longjmp' or `vfork'" */
@ -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...");

View File

@ -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 */

View File

@ -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 */

5
unix.c
View File

@ -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 */