diff options
author | Michael Lutz <michi@icosahedron.de> | 2019-03-17 01:59:46 +0100 |
---|---|---|
committer | Michael Lutz <michi@icosahedron.de> | 2019-04-06 11:27:39 +0200 |
commit | 05bc2ed7cbe07cb4cd535932f10778b35f72e944 (patch) | |
tree | 0faaf12fd1bafb0786236ffc82052e8b83dfca60 /src | |
parent | 05f4e7360886e36b221ef5c3af4426625a3de686 (diff) | |
download | openttd-05bc2ed7cbe07cb4cd535932f10778b35f72e944.tar.xz |
Codechange: Replace custom thread code with C++11 thread objects.
We assume a conforming C++11 compiler environment that has a valid <thread>-header.
Failure to run a real thread is handled gracefully.
Diffstat (limited to 'src')
28 files changed, 184 insertions, 545 deletions
diff --git a/src/genworld.cpp b/src/genworld.cpp index 64caf3b89..ee8768b62 100644 --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -34,7 +34,7 @@ #include "game/game.hpp" #include "game/game_instance.hpp" #include "string_func.h" -#include "thread/thread.h" +#include "thread.h" #include "safeguards.h" @@ -94,7 +94,7 @@ static void CleanupGeneration() /** * The internal, real, generate function. */ -static void _GenerateWorld(void *) +static void _GenerateWorld() { /* Make sure everything is done via OWNER_NONE. */ Backup<CompanyByte> _cur_company(_current_company, OWNER_NONE, FILE_LINE); @@ -242,14 +242,12 @@ void GenerateWorldSetAbortCallback(GWAbortProc *proc) */ void WaitTillGeneratedWorld() { - if (_gw.thread == NULL) return; + if (!_gw.thread.joinable()) return; _modal_progress_work_mutex.unlock(); _modal_progress_paint_mutex.unlock(); _gw.quit_thread = true; - _gw.thread->Join(); - delete _gw.thread; - _gw.thread = NULL; + _gw.thread.join(); _gw.threaded = false; _modal_progress_work_mutex.lock(); _modal_progress_paint_mutex.lock(); @@ -284,7 +282,7 @@ void HandleGeneratingWorldAbortion() CleanupGeneration(); - if (_gw.thread != NULL) _gw.thread->Exit(); + if (_gw.thread.joinable() && _gw.thread.get_id() == std::this_thread::get_id()) throw OTTDThreadExitSignal(); SwitchToMode(_switch_mode); } @@ -326,17 +324,13 @@ void GenerateWorld(GenWorldMode mode, uint size_x, uint size_y, bool reset_setti SetupColoursAndInitialWindow(); SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0); - if (_gw.thread != NULL) { - _gw.thread->Join(); - delete _gw.thread; - _gw.thread = NULL; - } + if (_gw.thread.joinable()) _gw.thread.join(); - if (!UseThreadedModelProgress() || !VideoDriver::GetInstance()->HasGUI() || !ThreadObject::New(&_GenerateWorld, NULL, &_gw.thread, "ottd:genworld")) { + if (!UseThreadedModelProgress() || !VideoDriver::GetInstance()->HasGUI() || !StartNewThread(&_gw.thread, "ottd:genworld", &_GenerateWorld)) { DEBUG(misc, 1, "Cannot create genworld thread, reverting to single-threaded mode"); _gw.threaded = false; _modal_progress_work_mutex.unlock(); - _GenerateWorld(NULL); + _GenerateWorld(); _modal_progress_work_mutex.lock(); return; } diff --git a/src/genworld.h b/src/genworld.h index 1b1c806e0..ce6b39bd9 100644 --- a/src/genworld.h +++ b/src/genworld.h @@ -13,6 +13,7 @@ #define GENWORLD_H #include "company_type.h" +#include <thread> /** Constants related to world generation */ enum LandscapeGenerator { @@ -61,7 +62,7 @@ struct GenWorldInfo { uint size_y; ///< Y-size of the map GWDoneProc *proc; ///< Proc that is called when done (can be NULL) GWAbortProc *abortp; ///< Proc that is called when aborting (can be NULL) - class ThreadObject *thread; ///< The thread we are in (can be NULL) + std::thread thread; ///< The thread we are in (joinable if a thread was created) }; /** Current stage of world generation process */ diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index fcc9dce78..5b8683820 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -39,7 +39,6 @@ LinkGraphJob::LinkGraphJob(const LinkGraph &orig) : * This is on purpose. */ link_graph(orig), settings(_settings_game.linkgraph), - thread(NULL), join_date(_date + _settings_game.linkgraph.recalc_time) { } @@ -61,8 +60,7 @@ void LinkGraphJob::EraseFlows(NodeID from) */ void LinkGraphJob::SpawnThread() { - if (!ThreadObject::New(&(LinkGraphSchedule::Run), this, &this->thread, "ottd:linkgraph")) { - this->thread = NULL; + if (!StartNewThread(&this->thread, "ottd:linkgraph", &(LinkGraphSchedule::Run), this)) { /* Of course this will hang a bit. * On the other hand, if you want to play games which make this hang noticably * on a platform without threads then you'll probably get other problems first. @@ -79,10 +77,8 @@ void LinkGraphJob::SpawnThread() */ void LinkGraphJob::JoinThread() { - if (this->thread != NULL) { - this->thread->Join(); - delete this->thread; - this->thread = NULL; + if (this->thread.joinable()) { + this->thread.join(); } } diff --git a/src/linkgraph/linkgraphjob.h b/src/linkgraph/linkgraphjob.h index b47dd70b9..e52d2412d 100644 --- a/src/linkgraph/linkgraphjob.h +++ b/src/linkgraph/linkgraphjob.h @@ -12,7 +12,7 @@ #ifndef LINKGRAPHJOB_H #define LINKGRAPHJOB_H -#include "../thread/thread.h" +#include "../thread.h" #include "linkgraph.h" #include <list> @@ -59,7 +59,7 @@ private: protected: const LinkGraph link_graph; ///< Link graph to by analyzed. Is copied when job is started and mustn't be modified later. const LinkGraphSettings settings; ///< Copy of _settings_game.linkgraph at spawn time. - ThreadObject *thread; ///< Thread the job is running in or NULL if it's running in the main thread. + std::thread thread; ///< Thread the job is running in or a default-constructed thread if it's running in the main thread. Date join_date; ///< Date when the job is to be joined. NodeAnnotationVector nodes; ///< Extra node data necessary for link graph calculation. EdgeAnnotationMatrix edges; ///< Extra edge data necessary for link graph calculation. @@ -266,7 +266,7 @@ public: * Bare constructor, only for save/load. link_graph, join_date and actually * settings have to be brutally const-casted in order to populate them. */ - LinkGraphJob() : settings(_settings_game.linkgraph), thread(NULL), + LinkGraphJob() : settings(_settings_game.linkgraph), join_date(INVALID_DATE) {} LinkGraphJob(const LinkGraph &orig); diff --git a/src/linkgraph/linkgraphschedule.cpp b/src/linkgraph/linkgraphschedule.cpp index 8c508d817..c5f797f18 100644 --- a/src/linkgraph/linkgraphschedule.cpp +++ b/src/linkgraph/linkgraphschedule.cpp @@ -69,13 +69,11 @@ void LinkGraphSchedule::JoinNext() } /** - * Run all handlers for the given Job. This method is tailored to - * ThreadObject::New. - * @param j Pointer to a link graph job. + * Run all handlers for the given Job. + * @param job Pointer to a link graph job. */ -/* static */ void LinkGraphSchedule::Run(void *j) +/* static */ void LinkGraphSchedule::Run(LinkGraphJob *job) { - LinkGraphJob *job = (LinkGraphJob *)j; for (uint i = 0; i < lengthof(instance.handlers); ++i) { instance.handlers[i]->Run(*job); } diff --git a/src/linkgraph/linkgraphschedule.h b/src/linkgraph/linkgraphschedule.h index ec22be316..b7dfa9793 100644 --- a/src/linkgraph/linkgraphschedule.h +++ b/src/linkgraph/linkgraphschedule.h @@ -53,7 +53,7 @@ public: static const uint SPAWN_JOIN_TICK = 21; ///< Tick when jobs are spawned or joined every day. static LinkGraphSchedule instance; - static void Run(void *j); + static void Run(LinkGraphJob *job); static void Clear(); void SpawnNext(); diff --git a/src/music/dmusic.cpp b/src/music/dmusic.cpp index 4b9a22c72..bf8657ef4 100644 --- a/src/music/dmusic.cpp +++ b/src/music/dmusic.cpp @@ -19,7 +19,7 @@ #include "../debug.h" #include "../os/windows/win32.h" #include "../core/mem_func.hpp" -#include "../thread/thread.h" +#include "../thread.h" #include "../fileio_func.h" #include "../base_media_base.h" #include "dmusic.h" @@ -139,7 +139,7 @@ static struct { } _playback; /** Handle to our worker thread. */ -static ThreadObject *_dmusic_thread = NULL; +static std::thread _dmusic_thread; /** Event to signal the thread that it should look at a state change. */ static HANDLE _thread_event = NULL; /** Lock access to playback data that is not thread-safe. */ @@ -597,7 +597,7 @@ static void TransmitNotesOff(IDirectMusicBuffer *buffer, REFERENCE_TIME block_ti Sleep(Clamp((block_time - cur_time) / MS_TO_REFTIME, 5, 1000)); } -static void MidiThreadProc(void *) +static void MidiThreadProc() { DEBUG(driver, 2, "DMusic: Entering playback thread"); @@ -1169,7 +1169,7 @@ const char *MusicDriver_DMusic::Start(const char * const *parm) _thread_event = CreateEvent(NULL, FALSE, FALSE, NULL); if (_thread_event == NULL) return "Can't create thread shutdown event"; - if (!ThreadObject::New(&MidiThreadProc, this, &_dmusic_thread, "ottd:dmusic")) return "Can't create MIDI output thread"; + if (!StartNewThread(&_dmusic_thread, "ottd:dmusic", &MidiThreadProc)) return "Can't create MIDI output thread"; return NULL; } @@ -1183,10 +1183,10 @@ MusicDriver_DMusic::~MusicDriver_DMusic() void MusicDriver_DMusic::Stop() { - if (_dmusic_thread != NULL) { + if (_dmusic_thread.joinable()) { _playback.shutdown = true; SetEvent(_thread_event); - _dmusic_thread->Join(); + _dmusic_thread.join(); } /* Unloaded any instruments we loaded. */ diff --git a/src/network/core/tcp.h b/src/network/core/tcp.h index 243ec042d..f9e1e00cb 100644 --- a/src/network/core/tcp.h +++ b/src/network/core/tcp.h @@ -63,7 +63,6 @@ public: */ class TCPConnecter { private: - class ThreadObject *thread; ///< Thread used to create the TCP connection bool connected; ///< Whether we succeeded in making the connection bool aborted; ///< Whether we bailed out (i.e. connection making failed) bool killed; ///< Whether we got killed @@ -71,7 +70,7 @@ private: void Connect(); - static void ThreadEntry(void *param); + static void ThreadEntry(TCPConnecter *param); protected: /** Address we're connecting to */ diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp index d76042821..d923688b1 100644 --- a/src/network/core/tcp_connect.cpp +++ b/src/network/core/tcp_connect.cpp @@ -12,7 +12,7 @@ */ #include "../../stdafx.h" -#include "../../thread/thread.h" +#include "../../thread.h" #include "tcp.h" @@ -33,7 +33,7 @@ TCPConnecter::TCPConnecter(const NetworkAddress &address) : address(address) { _tcp_connecters.push_back(this); - if (!ThreadObject::New(TCPConnecter::ThreadEntry, this, &this->thread, "ottd:tcp")) { + if (!StartNewThread(NULL, "ottd:tcp", &TCPConnecter::ThreadEntry, this)) { this->Connect(); } } @@ -53,9 +53,9 @@ void TCPConnecter::Connect() * Entry point for the new threads. * @param param the TCPConnecter instance to call Connect on. */ -/* static */ void TCPConnecter::ThreadEntry(void *param) +/* static */ void TCPConnecter::ThreadEntry(TCPConnecter *param) { - static_cast<TCPConnecter*>(param)->Connect(); + param->Connect(); } /** diff --git a/src/network/network_server.h b/src/network/network_server.h index f35192a11..f13691fcd 100644 --- a/src/network/network_server.h +++ b/src/network/network_server.h @@ -14,7 +14,6 @@ #include "network_internal.h" #include "core/tcp_listen.h" -#include "../thread/thread.h" class ServerNetworkGameSocketHandler; /** Make the code look slightly nicer/simpler. */ diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index df8c59aac..7674036e7 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -24,7 +24,7 @@ #include "network.h" #include "../core/endian_func.hpp" #include "../company_base.h" -#include "../thread/thread.h" +#include "../thread.h" #include "../rev.h" #include "../newgrf_text.h" #include "../strings_func.h" @@ -49,35 +49,19 @@ NetworkUDPSocketHandler *_udp_client_socket = NULL; ///< udp client socket NetworkUDPSocketHandler *_udp_server_socket = NULL; ///< udp server socket NetworkUDPSocketHandler *_udp_master_socket = NULL; ///< udp master socket -/** Simpler wrapper struct for NetworkUDPQueryServerThread */ -struct NetworkUDPQueryServerInfo : NetworkAddress { - bool manually; ///< Did we connect manually or not? - - /** - * Create the structure. - * @param address The address of the server to query. - * @param manually Whether the address was entered manually. - */ - NetworkUDPQueryServerInfo(const NetworkAddress &address, bool manually) : - NetworkAddress(address), - manually(manually) - { - } -}; - /** * Helper function doing the actual work for querying the server. * @param address The address of the server. * @param needs_mutex Whether we need to acquire locks when sending the packet or not. * @param manually Whether the address was entered manually. */ -static void NetworkUDPQueryServer(NetworkAddress *address, bool needs_mutex, bool manually) +static void DoNetworkUDPQueryServer(NetworkAddress &address, bool needs_mutex, bool manually) { /* Clear item in gamelist */ NetworkGameList *item = CallocT<NetworkGameList>(1); - address->GetAddressAsString(item->info.server_name, lastof(item->info.server_name)); - strecpy(item->info.hostname, address->GetHostname(), lastof(item->info.hostname)); - item->address = *address; + address.GetAddressAsString(item->info.server_name, lastof(item->info.server_name)); + strecpy(item->info.hostname, address.GetHostname(), lastof(item->info.hostname)); + item->address = address; item->manually = manually; NetworkGameListAddItemDelayed(item); @@ -85,19 +69,7 @@ static void NetworkUDPQueryServer(NetworkAddress *address, bool needs_mutex, boo if (needs_mutex) lock.lock(); /* Init the packet */ Packet p(PACKET_UDP_CLIENT_FIND_SERVER); - if (_udp_client_socket != NULL) _udp_client_socket->SendPacket(&p, address); -} - -/** - * Threaded part for resolving the IP of a server and querying it. - * @param pntr the NetworkUDPQueryServerInfo. - */ -static void NetworkUDPQueryServerThread(void *pntr) -{ - NetworkUDPQueryServerInfo *info = (NetworkUDPQueryServerInfo*)pntr; - NetworkUDPQueryServer(info, true, info->manually); - - delete info; + if (_udp_client_socket != NULL) _udp_client_socket->SendPacket(&p, &address); } /** @@ -107,9 +79,8 @@ static void NetworkUDPQueryServerThread(void *pntr) */ void NetworkUDPQueryServer(NetworkAddress address, bool manually) { - NetworkUDPQueryServerInfo *info = new NetworkUDPQueryServerInfo(address, manually); - if (address.IsResolved() || !ThreadObject::New(NetworkUDPQueryServerThread, info, NULL, "ottd:udp-query")) { - NetworkUDPQueryServerThread(info); + if (address.IsResolved() || !StartNewThread(NULL, "ottd:udp-query", &DoNetworkUDPQueryServer, std::move(address), true, std::move(manually))) { + DoNetworkUDPQueryServer(address, true, manually); } } @@ -429,7 +400,7 @@ void ClientNetworkUDPSocketHandler::Receive_MASTER_RESPONSE_LIST(Packet *p, Netw /* Somehow we reached the end of the packet */ if (this->HasClientQuit()) return; - NetworkUDPQueryServer(&addr, false, false); + DoNetworkUDPQueryServer(addr, false, false); } } } @@ -535,9 +506,8 @@ void NetworkUDPSearchGame() /** * Thread entry point for de-advertising. - * @param pntr unused. */ -static void NetworkUDPRemoveAdvertiseThread(void *pntr) +static void NetworkUDPRemoveAdvertiseThread() { DEBUG(net, 1, "[udp] removing advertise from master server"); @@ -563,16 +533,15 @@ void NetworkUDPRemoveAdvertise(bool blocking) /* Check if we are advertising */ if (!_networking || !_network_server || !_network_udp_server) return; - if (blocking || !ThreadObject::New(NetworkUDPRemoveAdvertiseThread, NULL, NULL, "ottd:udp-advert")) { - NetworkUDPRemoveAdvertiseThread(NULL); + if (blocking || !StartNewThread(NULL, "ottd:udp-advert", &NetworkUDPRemoveAdvertiseThread)) { + NetworkUDPRemoveAdvertiseThread(); } } /** * Thread entry point for advertising. - * @param pntr unused. */ -static void NetworkUDPAdvertiseThread(void *pntr) +static void NetworkUDPAdvertiseThread() { /* Find somewhere to send */ NetworkAddress out_addr(NETWORK_MASTER_SERVER_HOST, NETWORK_MASTER_SERVER_PORT); @@ -645,8 +614,8 @@ void NetworkUDPAdvertise() if (_next_advertisement < _last_advertisement) _next_advertisement = UINT32_MAX; if (_next_retry < _last_advertisement) _next_retry = UINT32_MAX; - if (!ThreadObject::New(NetworkUDPAdvertiseThread, NULL, NULL, "ottd:udp-advert")) { - NetworkUDPAdvertiseThread(NULL); + if (!StartNewThread(NULL, "ottd:udp-advert", &NetworkUDPAdvertiseThread)) { + NetworkUDPAdvertiseThread(); } } diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp index feb23648a..acde37063 100644 --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -21,7 +21,8 @@ #include "video/video_driver.hpp" #include "strings_func.h" #include "textfile_gui.h" -#include "thread/thread.h" +#include "thread.h" +#include "newgrf_config.h" #include "fileio_func.h" #include "fios.h" @@ -724,7 +725,7 @@ static int CDECL GRFSorter(GRFConfig * const *p1, GRFConfig * const *p2) * Really perform the scan for all NewGRFs. * @param callback The callback to call after the scanning is complete. */ -void DoScanNewGRFFiles(void *callback) +void DoScanNewGRFFiles(NewGRFScanCallback *callback) { std::unique_lock<std::mutex> lock_work(_modal_progress_work_mutex); @@ -767,7 +768,7 @@ void DoScanNewGRFFiles(void *callback) /* Yes... these are the NewGRF windows */ InvalidateWindowClassesData(WC_SAVELOAD, 0, true); InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_NEWGRF_STATE, GOID_NEWGRF_RESCANNED, true); - if (callback != NULL) ((NewGRFScanCallback*)callback)->OnNewGRFsScanned(); + if (callback != NULL) callback->OnNewGRFsScanned(); DeleteWindowByClass(WC_MODAL_PROGRESS); SetModalProgress(false); @@ -785,7 +786,7 @@ void ScanNewGRFFiles(NewGRFScanCallback *callback) /* Only then can we really start, especially by marking the whole screen dirty. Get those other windows hidden!. */ MarkWholeScreenDirty(); - if (!UseThreadedModelProgress() || !VideoDriver::GetInstance()->HasGUI() || !ThreadObject::New(&DoScanNewGRFFiles, callback, NULL, "ottd:newgrf-scan")) { + if (!UseThreadedModelProgress() || !VideoDriver::GetInstance()->HasGUI() || !StartNewThread(NULL, "ottd:newgrf-scan", &DoScanNewGRFFiles, (NewGRFScanCallback *)callback)) { // Without the seemingly superfluous cast, strange compiler errors ensue. _modal_progress_work_mutex.unlock(); _modal_progress_paint_mutex.unlock(); DoScanNewGRFFiles(callback); diff --git a/src/openttd.cpp b/src/openttd.cpp index 180de64b0..9fd672916 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -857,6 +857,7 @@ int openttd_main(int argc, char *argv[]) VideoDriver::GetInstance()->MainLoop(); WaitTillSaved(); + WaitTillGeneratedWorld(); // Make sure any generate world threads have been joined. /* only save config if we have to */ if (save_config) { diff --git a/src/os/os2/os2.cpp b/src/os/os2/os2.cpp index 7b34f528a..9d01a99b8 100644 --- a/src/os/os2/os2.cpp +++ b/src/os/os2/os2.cpp @@ -18,6 +18,7 @@ #include "../../core/random_func.hpp" #include "../../string_func.h" #include "../../textbuf_gui.h" +#include "../../thread.h" #include "table/strings.h" @@ -226,3 +227,7 @@ void OSOpenBrowser(const char *url) // stub only DEBUG(misc, 0, "Failed to open url: %s", url); } + +void SetCurrentThreadName(const char *) +{ +} diff --git a/src/os/unix/unix.cpp b/src/os/unix/unix.cpp index 2f982dea0..024dc8627 100644 --- a/src/os/unix/unix.cpp +++ b/src/os/unix/unix.cpp @@ -17,6 +17,7 @@ #include "../../debug.h" #include "../../string_func.h" #include "../../fios.h" +#include "../../thread.h" #include <dirent.h> @@ -43,11 +44,17 @@ #include <sys/sysctl.h> #endif +#ifndef NO_THREADS +#include <pthread.h> +#endif + #if defined(__APPLE__) - #if defined(WITH_SDL) +# if defined(WITH_SDL) /* the mac implementation needs this file included in the same file as main() */ - #include <SDL.h> - #endif +# include <SDL.h> +# endif + +# include "../macosx/macos.h" #endif #include "../../safeguards.h" @@ -317,4 +324,15 @@ void OSOpenBrowser(const char *url) DEBUG(misc, 0, "Failed to open url: %s", url); exit(0); } -#endif +#endif /* __APPLE__ */ + +void SetCurrentThreadName(const char *threadName) { +#if !defined(NO_THREADS) && defined(__GLIBC__) +#if __GLIBC_PREREQ(2, 12) + if (threadName) pthread_setname_np(pthread_self(), threadName); +#endif /* __GLIBC_PREREQ(2, 12) */ +#endif /* !defined(NO_THREADS) && defined(__GLIBC__) */ +#if defined(__APPLE__) + MacOSSetThreadName(threadName); +#endif /* defined(__APPLE__) */ +} diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index 4a4955549..2a72ca1c5 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -30,6 +30,7 @@ #include <errno.h> #include <sys/stat.h> #include "../../language.h" +#include "../../thread.h" #include "../../safeguards.h" @@ -816,12 +817,12 @@ PACK_N(struct THREADNAME_INFO { /** * Signal thread name to any attached debuggers. */ -void SetWin32ThreadName(DWORD dwThreadID, const char* threadName) +void SetCurrentThreadName(const char *threadName) { THREADNAME_INFO info; info.dwType = 0x1000; info.szName = threadName; - info.dwThreadID = dwThreadID; + info.dwThreadID = -1; info.dwFlags = 0; #pragma warning(push) @@ -832,4 +833,6 @@ void SetWin32ThreadName(DWORD dwThreadID, const char* threadName) } #pragma warning(pop) } +#else +void SetCurrentThreadName(const char *) {} #endif diff --git a/src/os/windows/win32.h b/src/os/windows/win32.h index 4f813c4a6..23e216762 100644 --- a/src/os/windows/win32.h +++ b/src/os/windows/win32.h @@ -39,12 +39,6 @@ HRESULT OTTDSHGetFolderPath(HWND, int, HANDLE, DWORD, LPTSTR); #define SHGFP_TYPE_CURRENT 0 #endif /* __MINGW32__ */ -#ifdef _MSC_VER -void SetWin32ThreadName(DWORD dwThreadID, const char* threadName); -#else -static inline void SetWin32ThreadName(DWORD dwThreadID, const char* threadName) {} -#endif - void Win32SetCurrentLocaleName(const char *iso_code); int OTTDStringCompare(const char *s1, const char *s2); diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index c97087224..95690f6af 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -26,7 +26,7 @@ #include "../stdafx.h" #include "../debug.h" #include "../station_base.h" -#include "../thread/thread.h" +#include "../thread.h" #include "../town.h" #include "../network/network.h" #include "../window_func.h" @@ -372,7 +372,7 @@ void NORETURN SlErrorCorruptFmt(const char *format, ...) typedef void (*AsyncSaveFinishProc)(); ///< Callback for when the savegame loading is finished. static AsyncSaveFinishProc _async_save_finish = NULL; ///< Callback to call when the savegame loading is finished. -static ThreadObject *_save_thread; ///< The thread we're using to compress and write a savegame +static std::thread _save_thread; ///< The thread we're using to compress and write a savegame /** * Called by save thread to tell we finished saving. @@ -397,10 +397,8 @@ void ProcessAsyncSaveFinish() _async_save_finish = NULL; - if (_save_thread != NULL) { - _save_thread->Join(); - delete _save_thread; - _save_thread = NULL; + if (_save_thread.joinable()) { + _save_thread.join(); } } @@ -2486,19 +2484,11 @@ static SaveOrLoadResult SaveFileToDisk(bool threaded) } } -/** Thread run function for saving the file to disk. */ -static void SaveFileToDiskThread(void *arg) -{ - SaveFileToDisk(true); -} - void WaitTillSaved() { - if (_save_thread == NULL) return; + if (!_save_thread.joinable()) return; - _save_thread->Join(); - delete _save_thread; - _save_thread = NULL; + _save_thread.join(); /* Make sure every other state is handled properly as well. */ ProcessAsyncSaveFinish(); @@ -2525,7 +2515,8 @@ static SaveOrLoadResult DoSave(SaveFilter *writer, bool threaded) SlSaveChunks(); SaveFileStart(); - if (!threaded || !ThreadObject::New(&SaveFileToDiskThread, NULL, &_save_thread, "ottd:savegame")) { + + if (!threaded || !StartNewThread(&_save_thread, "ottd:savegame", &SaveFileToDisk, true)) { if (threaded) DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode..."); SaveOrLoadResult result = SaveFileToDisk(false); diff --git a/src/sound/win32_s.cpp b/src/sound/win32_s.cpp index c9c1a8afd..493ed7fc1 100644 --- a/src/sound/win32_s.cpp +++ b/src/sound/win32_s.cpp @@ -20,6 +20,7 @@ #include <windows.h> #include <mmsystem.h> #include "../os/windows/win32.h" +#include "../thread.h" #include "../safeguards.h" @@ -42,7 +43,7 @@ static void PrepareHeader(WAVEHDR *hdr) static DWORD WINAPI SoundThread(LPVOID arg) { - SetWin32ThreadName(-1, "ottd:win-sound"); + SetCurrentThreadName("ottd:win-sound"); do { for (WAVEHDR *hdr = _wave_hdr; hdr != endof(_wave_hdr); hdr++) { diff --git a/src/thread.h b/src/thread.h new file mode 100644 index 000000000..086e2f51e --- /dev/null +++ b/src/thread.h @@ -0,0 +1,78 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** @file thread.h Base of all threads. */ + +#ifndef THREAD_H +#define THREAD_H + +#include "debug.h" +#include <system_error> +#include <thread> + +/** Signal used for signalling we knowingly want to end the thread. */ +class OTTDThreadExitSignal { }; + + +/** + * Get number of processor cores in the system, including HyperThreading or similar. + * @return Total number of processor cores. + */ +uint GetCPUCoreCount(); + +/** + * Name the thread this function is called on for the debugger. + * @param name Name to set for the thread.. + */ +void SetCurrentThreadName(const char *name); + + +/** + * Start a new thread. + * @tparam TFn Type of the function to call on the thread. + * @tparam TArgs Type of the parameters of the thread function. + * @param thr Pointer to a thread object; may be \c NULL if a detached thread is wanted. + * @param name Name of the thread. + * @param _Fx Function to call on the thread. + * @param _Ax Arguments for the thread function. + * @return True if the thread was successfully started, false otherwise. + */ +template<class TFn, class... TArgs> +inline bool StartNewThread(std::thread *thr, const char *name, TFn&& _Fx, TArgs&&... _Ax) +{ +#ifndef NO_THREADS + try { + std::thread t([] (const char *name, TFn&& F, TArgs&&... A) { + SetCurrentThreadName(name); + try { + /* Call user function with the given arguments. */ + F(A...); + } catch (OTTDThreadExitSignal&) { + } catch (...) { + NOT_REACHED(); + } + }, name, std::forward<TFn>(_Fx), std::forward<TArgs>(_Ax)...); + + if (thr != NULL) { + *thr = std::move(t); + } else { + t.detach(); + } + + return true; + } catch (const std::system_error& e) { + /* Something went wrong, the system we are running on might not support threads. */ + DEBUG(misc, 1, "Can't create thread '%s': %s", name, e.what()); + } +#endif + + return false; +} + +#endif /* THREAD_H */ diff --git a/src/thread/thread.h b/src/thread/thread.h deleted file mode 100644 index eca825e25..000000000 --- a/src/thread/thread.h +++ /dev/null @@ -1,59 +0,0 @@ -/* $Id$ */ - -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. - */ - -/** @file thread.h Base of all threads. */ - -#ifndef THREAD_H -#define THREAD_H - -/** Definition of all thread entry functions. */ -typedef void (*OTTDThreadFunc)(void *); - -/** Signal used for signalling we knowingly want to end the thread. */ -class OTTDThreadExitSignal { }; - -/** - * A Thread Object which works on all our supported OSes. - */ -class ThreadObject { -public: - /** - * Virtual destructor to allow 'delete' operator to work properly. - */ - virtual ~ThreadObject() {}; - - /** - * Exit this thread. - */ - virtual bool Exit() = 0; - - /** - * Join this thread. - */ - virtual void Join() = 0; - - /** - * Create a thread; proc will be called as first function inside the thread, - * with optional params. - * @param proc The procedure to call inside the thread. - * @param param The params to give with 'proc'. - * @param thread Place to store a pointer to the thread in. May be NULL. - * @param name A name for the thread. May be NULL. - * @return True if the thread was started correctly. - */ - static bool New(OTTDThreadFunc proc, void *param, ThreadObject **thread = NULL, const char *name = NULL); -}; - -/** - * Get number of processor cores in the system, including HyperThreading or similar. - * @return Total number of processor cores. - */ -uint GetCPUCoreCount(); - -#endif /* THREAD_H */ diff --git a/src/thread/thread_none.cpp b/src/thread/thread_none.cpp deleted file mode 100644 index 83ae52d46..000000000 --- a/src/thread/thread_none.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* $Id$ */ - -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. - */ - -/** @file thread_none.cpp No-Threads-Available implementation of Threads */ - -#include "../stdafx.h" -#include "thread.h" - -#include "../safeguards.h" - -/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name) -{ - if (thread != NULL) *thread = NULL; - return false; -} - -/** Mutex that doesn't do locking because it ain't needed when there're no threads */ -class ThreadMutex_None : public ThreadMutex { -public: - virtual void BeginCritical(bool allow_recursive = false) {} - virtual void EndCritical(bool allow_recursive = false) {} - virtual void WaitForSignal() {} - virtual void SendSignal() {} -}; diff --git a/src/thread/thread_os2.cpp b/src/thread/thread_os2.cpp deleted file mode 100644 index 72ee080d6..000000000 --- a/src/thread/thread_os2.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* $Id$ */ - -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. - */ - -/** @file thread_os2.cpp OS/2 implementation of Threads. */ - -#include "../stdafx.h" -#include "thread.h" - -#define INCL_DOS -#include <os2.h> -#include <process.h> - -#include "../safeguards.h" - -/** - * OS/2 version for ThreadObject. - */ -class ThreadObject_OS2 : public ThreadObject { -private: - TID thread; ///< System thread identifier. - OTTDThreadFunc proc; ///< External thread procedure. - void *param; ///< Parameter for the external thread procedure. - bool self_destruct; ///< Free ourselves when done? - -public: - /** - * Create a thread and start it, calling proc(param). - */ - ThreadObject_OS2(OTTDThreadFunc proc, void *param, bool self_destruct) : - thread(0), - proc(proc), - param(param), - self_destruct(self_destruct) - { - thread = _beginthread(stThreadProc, NULL, 1048576, this); - } - - bool Exit() override - { - _endthread(); - return true; - } - - void Join() override - { - DosWaitThread(&this->thread, DCWW_WAIT); - this->thread = 0; - } -private: - /** - * On thread creation, this function is called, which calls the real startup - * function. This to get back into the correct instance again. - */ - static void stThreadProc(void *thr) - { - ((ThreadObject_OS2 *)thr)->ThreadProc(); - } - - /** - * A new thread is created, and this function is called. Call the custom - * function of the creator of the thread. - */ - void ThreadProc() - { - /* Call the proc of the creator to continue this thread */ - try { - this->proc(this->param); - } catch (OTTDThreadExitSignal e) { - } catch (...) { - NOT_REACHED(); - } - - if (self_destruct) { - this->Exit(); - delete this; - } - } -}; - -/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name) -{ - ThreadObject *to = new ThreadObject_OS2(proc, param, thread == NULL); - if (thread != NULL) *thread = to; - return true; -} diff --git a/src/thread/thread_pthread.cpp b/src/thread/thread_pthread.cpp deleted file mode 100644 index 50fefb531..000000000 --- a/src/thread/thread_pthread.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* $Id$ */ - -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. - */ - -/** @file thread_pthread.cpp POSIX pthread implementation of Threads. */ - -#include "../stdafx.h" -#include "thread.h" -#include <pthread.h> -#include <errno.h> - -#if defined(__APPLE__) -#include "../os/macosx/macos.h" -#endif - -#include "../safeguards.h" - -/** - * POSIX pthread version for ThreadObject. - */ -class ThreadObject_pthread : public ThreadObject { -private: - pthread_t thread; ///< System thread identifier. - OTTDThreadFunc proc; ///< External thread procedure. - void *param; ///< Parameter for the external thread procedure. - bool self_destruct; ///< Free ourselves when done? - const char *name; ///< Name for the thread - -public: - /** - * Create a pthread and start it, calling proc(param). - */ - ThreadObject_pthread(OTTDThreadFunc proc, void *param, bool self_destruct, const char *name) : - thread(0), - proc(proc), - param(param), - self_destruct(self_destruct), - name(name) - { - pthread_create(&this->thread, NULL, &stThreadProc, this); - } - - bool Exit() override - { - assert(pthread_self() == this->thread); - /* For now we terminate by throwing an error, gives much cleaner cleanup */ - throw OTTDThreadExitSignal(); - } - - void Join() override - { - /* You cannot join yourself */ - assert(pthread_self() != this->thread); - pthread_join(this->thread, NULL); - this->thread = 0; - } -private: - /** - * On thread creation, this function is called, which calls the real startup - * function. This to get back into the correct instance again. - */ - static void *stThreadProc(void *thr) - { - ThreadObject_pthread *self = (ThreadObject_pthread *) thr; -#if defined(__GLIBC__) -#if __GLIBC_PREREQ(2, 12) - if (self->name) { - pthread_setname_np(pthread_self(), self->name); - } -#endif -#endif -#if defined(__APPLE__) - MacOSSetThreadName(self->name); -#endif - self->ThreadProc(); - pthread_exit(NULL); - } - - /** - * A new thread is created, and this function is called. Call the custom - * function of the creator of the thread. - */ - void ThreadProc() - { - /* Call the proc of the creator to continue this thread */ - try { - this->proc(this->param); - } catch (OTTDThreadExitSignal) { - } catch (...) { - NOT_REACHED(); - } - - if (self_destruct) { - pthread_detach(pthread_self()); - delete this; - } - } -}; - -/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name) -{ - ThreadObject *to = new ThreadObject_pthread(proc, param, thread == NULL, name); - if (thread != NULL) *thread = to; - return true; -} diff --git a/src/thread/thread_win32.cpp b/src/thread/thread_win32.cpp deleted file mode 100644 index fc7a85a91..000000000 --- a/src/thread/thread_win32.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* $Id$ */ - -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. - */ - -/** @file thread_win32.cpp Win32 thread implementation of Threads. */ - -#include "../stdafx.h" -#include "thread.h" -#include "../debug.h" -#include "../core/alloc_func.hpp" -#include <stdlib.h> -#include <windows.h> -#include <process.h> -#include "../os/windows/win32.h" - -#include "../safeguards.h" - -/** - * Win32 thread version for ThreadObject. - */ -class ThreadObject_Win32 : public ThreadObject { -private: - HANDLE thread; ///< System thread identifier. - uint id; ///< Thread identifier. - OTTDThreadFunc proc; ///< External thread procedure. - void *param; ///< Parameter for the external thread procedure. - bool self_destruct; ///< Free ourselves when done? - const char *name; ///< Thread name. - -public: - /** - * Create a win32 thread and start it, calling proc(param). - */ - ThreadObject_Win32(OTTDThreadFunc proc, void *param, bool self_destruct, const char *name) : - thread(NULL), - id(0), - proc(proc), - param(param), - self_destruct(self_destruct), - name(name) - { - this->thread = (HANDLE)_beginthreadex(NULL, 0, &stThreadProc, this, CREATE_SUSPENDED, &this->id); - if (this->thread == NULL) return; - ResumeThread(this->thread); - } - - ~ThreadObject_Win32() override - { - if (this->thread != NULL) { - CloseHandle(this->thread); - this->thread = NULL; - } - } - - bool Exit() override - { - assert(GetCurrentThreadId() == this->id); - /* For now we terminate by throwing an error, gives much cleaner cleanup */ - throw OTTDThreadExitSignal(); - } - - void Join() override - { - /* You cannot join yourself */ - assert(GetCurrentThreadId() != this->id); - WaitForSingleObject(this->thread, INFINITE); - } - -private: - /** - * On thread creation, this function is called, which calls the real startup - * function. This to get back into the correct instance again. - */ - static uint CALLBACK stThreadProc(void *thr) - { - ((ThreadObject_Win32 *)thr)->ThreadProc(); - return 0; - } - - /** - * A new thread is created, and this function is called. Call the custom - * function of the creator of the thread. - */ - void ThreadProc() - { -#ifdef _MSC_VER - /* Set thread name for debuggers. Has to be done from the thread due to a race condition in older MS debuggers. */ - SetWin32ThreadName(-1, this->name); -#endif - try { - this->proc(this->param); - } catch (OTTDThreadExitSignal) { - } catch (...) { - NOT_REACHED(); - } - - if (self_destruct) delete this; - } -}; - -/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name) -{ - ThreadObject *to = new ThreadObject_Win32(proc, param, thread == NULL, name); - if (thread != NULL) *thread = to; - return true; -} diff --git a/src/video/dedicated_v.cpp b/src/video/dedicated_v.cpp index 58d7d04e0..e0096c893 100644 --- a/src/video/dedicated_v.cpp +++ b/src/video/dedicated_v.cpp @@ -73,6 +73,7 @@ static void DedicatedSignalHandler(int sig) # include <time.h> # include <tchar.h> # include "../os/windows/win32.h" +# include "../thread.h" static HANDLE _hInputReady, _hWaitForInputHandling; static HANDLE _hThread; // Thread to close static char _win_console_thread_buffer[200]; @@ -80,7 +81,7 @@ static char _win_console_thread_buffer[200]; /* Windows Console thread. Just loop and signal when input has been received */ static void WINAPI CheckForConsoleInput() { - SetWin32ThreadName(-1, "ottd:win-console"); + SetCurrentThreadName("ottd:win-console"); DWORD nb; HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); diff --git a/src/video/sdl_v.cpp b/src/video/sdl_v.cpp index b1609f7b3..5e3fb4552 100644 --- a/src/video/sdl_v.cpp +++ b/src/video/sdl_v.cpp @@ -17,7 +17,7 @@ #include "../rev.h" #include "../blitter/factory.hpp" #include "../network/network.h" -#include "../thread/thread.h" +#include "../thread.h" #include "../progress.h" #include "../core/random_func.hpp" #include "../core/math_func.hpp" @@ -38,8 +38,6 @@ static bool _all_modes; /** Whether the drawing is/may be done in a separate thread. */ static bool _draw_threaded; -/** Thread used to 'draw' to the screen, i.e. push data to the screen. */ -static ThreadObject *_draw_thread = NULL; /** Mutex to keep the access to the shared memory controlled. */ static std::recursive_mutex *_draw_mutex = NULL; /** Signal to draw the next frame. */ @@ -173,7 +171,7 @@ static void DrawSurfaceToScreen() } } -static void DrawSurfaceToScreenThread(void *) +static void DrawSurfaceToScreenThread() { /* First tell the main thread we're started */ std::unique_lock<std::recursive_mutex> lock(*_draw_mutex); @@ -188,8 +186,6 @@ static void DrawSurfaceToScreenThread(void *) DrawSurfaceToScreen(); _draw_signal->wait(lock); } - - _draw_thread->Exit(); } static const Dimension _default_resolutions[] = { @@ -671,6 +667,7 @@ void VideoDriver_SDL::MainLoop() CheckPaletteAnim(); + std::thread draw_thread; std::unique_lock<std::recursive_mutex> draw_lock; if (_draw_threaded) { /* Initialise the mutex first, because that's the thing we *need* @@ -683,7 +680,7 @@ void VideoDriver_SDL::MainLoop() _draw_signal = new std::condition_variable_any(); _draw_continue = true; - _draw_threaded = ThreadObject::New(&DrawSurfaceToScreenThread, NULL, &_draw_thread, "ottd:draw-sdl"); + _draw_threaded = StartNewThread(&draw_thread, "ottd:draw-sdl", &DrawSurfaceToScreenThread); /* Free the mutex if we won't be able to use it. */ if (!_draw_threaded) { @@ -795,15 +792,13 @@ void VideoDriver_SDL::MainLoop() _draw_signal->notify_one(); if (draw_lock.owns_lock()) draw_lock.unlock(); draw_lock.release(); - _draw_thread->Join(); + draw_thread.join(); delete _draw_mutex; delete _draw_signal; - delete _draw_thread; _draw_mutex = NULL; _draw_signal = NULL; - _draw_thread = NULL; } } diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index ef7bc89e2..5aec329b2 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -19,7 +19,7 @@ #include "../core/math_func.hpp" #include "../core/random_func.hpp" #include "../texteff.hpp" -#include "../thread/thread.h" +#include "../thread.h" #include "../progress.h" #include "../window_gui.h" #include "../window_func.h" @@ -67,8 +67,6 @@ DWORD _imm_props; /** Whether the drawing is/may be done in a separate thread. */ static bool _draw_threaded; -/** Thread used to 'draw' to the screen, i.e. push data to the screen. */ -static ThreadObject *_draw_thread = NULL; /** Mutex to keep the access to the shared memory controlled. */ static std::recursive_mutex *_draw_mutex = NULL; /** Signal to draw the next frame. */ @@ -395,7 +393,7 @@ static void PaintWindow(HDC dc) DeleteDC(dc2); } -static void PaintWindowThread(void *) +static void PaintWindowThread() { /* First tell the main thread we're started */ std::unique_lock<std::recursive_mutex> lock(*_draw_mutex); @@ -426,8 +424,6 @@ static void PaintWindowThread(void *) _draw_signal->wait(*_draw_mutex); } - - _draw_thread->Exit(); } /** Forward key presses to the window system. */ @@ -1190,6 +1186,7 @@ void VideoDriver_Win32::MainLoop() uint32 last_cur_ticks = cur_ticks; uint32 next_tick = cur_ticks + MILLISECONDS_PER_TICK; + std::thread draw_thread; std::unique_lock<std::recursive_mutex> draw_lock; if (_draw_threaded) { @@ -1206,7 +1203,7 @@ void VideoDriver_Win32::MainLoop() draw_lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex); _draw_continue = true; - _draw_threaded = ThreadObject::New(&PaintWindowThread, NULL, &_draw_thread, "ottd:draw-win32"); + _draw_threaded = StartNewThread(&draw_thread, "ottd:draw-win32", &PaintWindowThread); /* Free the mutex if we won't be able to use it. */ if (!_draw_threaded) { @@ -1308,11 +1305,10 @@ void VideoDriver_Win32::MainLoop() _draw_signal->notify_all(); if (draw_lock.owns_lock()) draw_lock.unlock(); draw_lock.release(); - _draw_thread->Join(); + draw_thread.join(); delete _draw_mutex; delete _draw_signal; - delete _draw_thread; _draw_mutex = NULL; } |