diff --git a/Makefile.src.in b/Makefile.src.in index 01c298d9ba..dc3a5f48b3 100644 --- a/Makefile.src.in +++ b/Makefile.src.in @@ -143,7 +143,7 @@ $(OBJS_CPP:%.o=%.d): %.d: $(SRC_DIR)/%.cpp $(FILE_DEP) $(OBJS_MM:%.o=%.d): %.d: $(SRC_DIR)/%.mm $(FILE_DEP) $(E) '$(STAGE) DEP $(<:$(SRC_DIR)/%.mm=%.mm)' - $(Q)$(CC_HOST) $(CFLAGS) -MM $< | sed 's@^$(@F:%.d=%.o):@$@ $(@:%.d=%.o):@' > $@ + $(Q)$(CXX_HOST) $(CFLAGS) $(CXXFLAGS) -MM $< | sed 's@^$(@F:%.d=%.o):@$@ $(@:%.d=%.o):@' > $@ $(OBJS_RC:%.o=%.d): %.d: $(SRC_DIR)/%.rc $(FILE_DEP) $(E) '$(STAGE) DEP $(<:$(SRC_DIR)/%.rc=%.rc)' @@ -253,7 +253,7 @@ $(filter %sse4.o, $(OBJS_CPP)): %.o: $(SRC_DIR)/%.cpp $(DEP_MASK) $(FILE_DEP) $(OBJS_MM): %.o: $(SRC_DIR)/%.mm $(DEP_MASK) $(FILE_DEP) $(E) '$(STAGE) Compiling $(<:$(SRC_DIR)/%.mm=%.mm)' - $(Q)$(CC_HOST) $(CFLAGS) -c -o $@ $< + $(Q)$(CXX_HOST) $(CFLAGS) $(CXXFLAGS) -c -o $@ $< $(OBJS_RC): %.o: $(SRC_DIR)/%.rc $(FILE_DEP) $(E) '$(STAGE) Compiling resource $(<:$(SRC_DIR)/%.rc=%.rc)' @@ -277,7 +277,7 @@ endif # Revision files -$(SRC_DIR)/rev.cpp: $(CONFIG_CACHE_VERSION) $(SRC_DIR)/rev.cpp.in +$(SRC_DIR)/rev.cpp: $(CONFIG_CACHE_VERSION) $(CONFIG_CACHE_INVOCATION) $(SRC_DIR)/rev.cpp.in $(Q)cat $(SRC_DIR)/rev.cpp.in | sed "s@\!\!ISODATE\!\!@$(ISODATE)@g;s@!!VERSION!!@$(VERSION)@g;s@!!MODIFIED!!@$(MODIFIED)@g;s@!!DATE!!@`date +%d.%m.%y`@g;s@!!GITHASH!!@$(GITHASH)@g;s@!!ISTAG!!@$(ISTAG)@g;s@!!ISSTABLETAG!!@$(ISSTABLETAG)@g;s@\!\!CONFIGURE_INVOCATION\!\!@$(CONFIGURE_INVOCATION)@g;" > $(SRC_DIR)/rev.cpp $(SRC_DIR)/os/windows/ottdres.rc: $(CONFIG_CACHE_VERSION) $(SRC_DIR)/os/windows/ottdres.rc.in diff --git a/projects/generate_vs142.vcxproj b/projects/generate_vs142.vcxproj index 3e55d7a1d4..7230676cb5 100644 --- a/projects/generate_vs142.vcxproj +++ b/projects/generate_vs142.vcxproj @@ -10,7 +10,6 @@ generate {2F31FD79-D1AC-43C4-89F3-B0D5E4E53E34} generate - 8.1 diff --git a/projects/openttd_vs140.vcxproj b/projects/openttd_vs140.vcxproj index 95611a60a5..58f9ac3967 100644 --- a/projects/openttd_vs140.vcxproj +++ b/projects/openttd_vs140.vcxproj @@ -1389,8 +1389,7 @@ - - + diff --git a/projects/openttd_vs140.vcxproj.filters b/projects/openttd_vs140.vcxproj.filters index 67311daee0..fa42454573 100644 --- a/projects/openttd_vs140.vcxproj.filters +++ b/projects/openttd_vs140.vcxproj.filters @@ -3258,12 +3258,9 @@ Windows files - + Threading - - Threading - Threading diff --git a/projects/openttd_vs141.vcxproj b/projects/openttd_vs141.vcxproj index 3d3c92b2d0..bd6ed6a3c8 100644 --- a/projects/openttd_vs141.vcxproj +++ b/projects/openttd_vs141.vcxproj @@ -1389,8 +1389,7 @@ - - + diff --git a/projects/openttd_vs141.vcxproj.filters b/projects/openttd_vs141.vcxproj.filters index 67311daee0..fa42454573 100644 --- a/projects/openttd_vs141.vcxproj.filters +++ b/projects/openttd_vs141.vcxproj.filters @@ -3258,12 +3258,9 @@ Windows files - + Threading - - Threading - Threading diff --git a/projects/openttd_vs142.vcxproj b/projects/openttd_vs142.vcxproj index 89dd51ee6b..7293e5eb4c 100644 --- a/projects/openttd_vs142.vcxproj +++ b/projects/openttd_vs142.vcxproj @@ -1389,8 +1389,7 @@ - - + diff --git a/projects/openttd_vs142.vcxproj.filters b/projects/openttd_vs142.vcxproj.filters index 67311daee0..fa42454573 100644 --- a/projects/openttd_vs142.vcxproj.filters +++ b/projects/openttd_vs142.vcxproj.filters @@ -3258,12 +3258,9 @@ Windows files - + Threading - - Threading - Threading diff --git a/projects/settingsgen_vs142.vcxproj b/projects/settingsgen_vs142.vcxproj index ca0418c15c..23a4e18562 100644 --- a/projects/settingsgen_vs142.vcxproj +++ b/projects/settingsgen_vs142.vcxproj @@ -10,7 +10,6 @@ settingsgen {E9548DE9-F089-49B7-93A6-30BE2CC311C7} settings - 8.1 diff --git a/projects/strgen_vs142.vcxproj b/projects/strgen_vs142.vcxproj index 5329d68daa..eb0657a30b 100644 --- a/projects/strgen_vs142.vcxproj +++ b/projects/strgen_vs142.vcxproj @@ -10,7 +10,6 @@ strgen {A133A442-BD0A-4ADE-B117-AD7545E4BDD1} strgen - 8.1 diff --git a/source.list b/source.list index 93d9ef6a07..b597abab24 100644 --- a/source.list +++ b/source.list @@ -1229,20 +1229,7 @@ sound/null_s.cpp #end # Threading -thread/thread.h -#if USE_THREADS - #if WIN32 - thread/thread_win32.cpp - #else - #if OS2 - thread/thread_os2.cpp - #else - thread/thread_pthread.cpp - #end - #end -#else - thread/thread_none.cpp -#end +thread.h tracerestrict.h tracerestrict.cpp diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp index 8b00de988e..2730070c32 100644 --- a/src/ai/ai_gui.cpp +++ b/src/ai/ai_gui.cpp @@ -1282,7 +1282,7 @@ struct AIDebugWindow : public Window { case WID_AID_RELOAD_TOGGLE: if (ai_debug_company == OWNER_DEITY) break; /* First kill the company of the AI, then start a new one. This should start the current AI again */ - DoCommandP(0, CCA_DELETE | ai_debug_company << 16, CRR_MANUAL, CMD_COMPANY_CTRL); + DoCommandP(0, CCA_DELETE | ai_debug_company << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL); DoCommandP(0, CCA_NEW_AI | ai_debug_company << 16, 0, CMD_COMPANY_CTRL); break; diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index 432a4fc69d..f75685a2bd 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -815,10 +815,9 @@ void CompanyAdminRemove(CompanyID company_id, CompanyRemoveReason reason) * @param flags operation to perform * @param p1 various functionality * - bits 0..15: CompanyCtrlAction - * - bits 16..24: CompanyID - * @param p2 various depending on CompanyCtrlAction - * - bits 0..31: ClientID (with CCA_NEW) - * - bits 0..1: CompanyRemoveReason (with CCA_DELETE) + * - bits 16..23: CompanyID + * - bits 24..31: CompanyRemoveReason (with CCA_DELETE) + * @param p2 ClientID * @param text unused * @return the cost of this operation or an error */ @@ -888,7 +887,7 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 } case CCA_DELETE: { // Delete a company - CompanyRemoveReason reason = (CompanyRemoveReason)GB(p2, 0, 2); + CompanyRemoveReason reason = (CompanyRemoveReason)GB(p1, 24, 8); if (reason >= CRR_END) return CMD_ERROR; Company *c = Company::GetIfValid(company_id); diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 9ba7fdf29b..31c84d85e3 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -828,7 +828,7 @@ DEF_CONSOLE_CMD(ConResetCompany) } /* It is safe to remove this company */ - DoCommandP(0, CCA_DELETE | index << 16, CRR_MANUAL, CMD_COMPANY_CTRL); + DoCommandP(0, CCA_DELETE | index << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL); IConsolePrint(CC_DEFAULT, "Company deleted."); return true; @@ -1203,7 +1203,7 @@ DEF_CONSOLE_CMD(ConReloadAI) } /* First kill the company of the AI, then start a new one. This should start the current AI again */ - DoCommandP(0, CCA_DELETE | company_id << 16, CRR_MANUAL, CMD_COMPANY_CTRL); + DoCommandP(0, CCA_DELETE | company_id << 16 | CRR_MANUAL << 24, 0,CMD_COMPANY_CTRL); DoCommandP(0, CCA_NEW_AI | company_id << 16, 0, CMD_COMPANY_CTRL); IConsolePrint(CC_DEFAULT, "AI reloaded."); @@ -1240,7 +1240,7 @@ DEF_CONSOLE_CMD(ConStopAI) } /* Now kill the company of the AI. */ - DoCommandP(0, CCA_DELETE | company_id << 16, CRR_MANUAL, CMD_COMPANY_CTRL); + DoCommandP(0, CCA_DELETE | company_id << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL); IConsolePrint(CC_DEFAULT, "AI stopped, company deleted."); return true; diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 379c8bd9e1..ea7cea70cb 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -31,7 +31,7 @@ #include "news_gui.h" #include "scope_info.h" #include "command_func.h" -#include "thread/thread.h" +#include "thread.h" #include "ai/ai_info.hpp" #include "game/game.hpp" @@ -394,7 +394,7 @@ char *CrashLog::FillCrashLog(char *buffer, const char *last) const if (IsNonMainThread()) { buffer += seprintf(buffer, last, "Non-main thread ("); - buffer += GetThreadName(buffer, last); + buffer += GetCurrentThreadName(buffer, last); buffer += seprintf(buffer, last, ")\n\n"); } diff --git a/src/economy.cpp b/src/economy.cpp index 49e0a48c16..4c11507854 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -672,7 +672,7 @@ static void CompanyCheckBankrupt(Company *c) * that changing the current company is okay. In case of single * player we are sure (the above check) that we are not the local * company and thus we won't be moved. */ - if (!_networking || _network_server) DoCommandP(0, CCA_DELETE | (c->index << 16), CRR_BANKRUPT, CMD_COMPANY_CTRL); + if (!_networking || _network_server) DoCommandP(0, CCA_DELETE | (c->index << 16) | (CRR_BANKRUPT << 24), 0, CMD_COMPANY_CTRL); break; } } diff --git a/src/genworld.cpp b/src/genworld.cpp index ed56a7034e..9bb2c75397 100644 --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -34,6 +34,7 @@ #include "game/game.hpp" #include "game/game_instance.hpp" #include "string_func.h" +#include "thread.h" #include "safeguards.h" @@ -93,14 +94,15 @@ static void CleanupGeneration() /** * The internal, real, generate function. */ -static void _GenerateWorld(void *) +static void _GenerateWorld() { /* Make sure everything is done via OWNER_NONE. */ Backup _cur_company(_current_company, OWNER_NONE, FILE_LINE); + std::unique_lock lock(_modal_progress_work_mutex, std::defer_lock); try { _generating_world = true; - _modal_progress_work_mutex->BeginCritical(); + lock.lock(); if (_network_dedicated) DEBUG(net, 1, "Generating map, please wait..."); /* Set the Random() seed to generation_seed so we produce the same map with the same seed */ if (_settings_game.game_creation.generation_seed == GENERATE_NEW_SEED) _settings_game.game_creation.generation_seed = _settings_newgame.game_creation.generation_seed = InteractiveRandom(); @@ -199,7 +201,7 @@ static void _GenerateWorld(void *) IncreaseGeneratingWorldProgress(GWP_GAME_START); CleanupGeneration(); - _modal_progress_work_mutex->EndCritical(); + lock.unlock(); ShowNewGRFError(); @@ -215,7 +217,6 @@ static void _GenerateWorld(void *) BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP, true); if (_cur_company.IsValid()) _cur_company.Restore(); _generating_world = false; - _modal_progress_work_mutex->EndCritical(); throw; } } @@ -246,17 +247,15 @@ void GenerateWorldSetAbortCallback(GWAbortProc *proc) */ void WaitTillGeneratedWorld() { - if (_gw.thread == NULL) return; + if (!_gw.thread.joinable()) return; - _modal_progress_work_mutex->EndCritical(); - _modal_progress_paint_mutex->EndCritical(); + _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->BeginCritical(); - _modal_progress_paint_mutex->BeginCritical(); + _modal_progress_work_mutex.lock(); + _modal_progress_paint_mutex.lock(); } /** @@ -288,7 +287,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); } @@ -330,18 +329,14 @@ 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 (!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->EndCritical(); - _GenerateWorld(NULL); - _modal_progress_work_mutex->BeginCritical(); + _modal_progress_work_mutex.unlock(); + _GenerateWorld(); + _modal_progress_work_mutex.lock(); return; } diff --git a/src/genworld.h b/src/genworld.h index 1b1c806e09..ce6b39bd99 100644 --- a/src/genworld.h +++ b/src/genworld.h @@ -13,6 +13,7 @@ #define GENWORLD_H #include "company_type.h" +#include /** 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/genworld_gui.cpp b/src/genworld_gui.cpp index e10d68e86f..691a464ed4 100644 --- a/src/genworld_gui.cpp +++ b/src/genworld_gui.cpp @@ -1377,10 +1377,10 @@ static void _SetGeneratingWorldProgress(GenWorldProgress cls, uint progress, uin * paint thread. The 'other' thread already has the paint thread rights so * this ensures us that we are waiting until the paint thread is done * before we reacquire the mapgen rights */ - _modal_progress_work_mutex->EndCritical(); - _modal_progress_paint_mutex->BeginCritical(); - _modal_progress_work_mutex->BeginCritical(); - _modal_progress_paint_mutex->EndCritical(); + _modal_progress_work_mutex.unlock(); + _modal_progress_paint_mutex.lock(); + _modal_progress_work_mutex.lock(); + _modal_progress_paint_mutex.unlock(); _gws.timer = _realtime_tick; } diff --git a/src/gfx.cpp b/src/gfx.cpp index b83683cd0c..39cd93844c 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -21,6 +21,7 @@ #include "network/network_func.h" #include "window_func.h" #include "newgrf_debug.h" +#include "thread.h" #include "table/palettes.h" #include "table/string_colours.h" @@ -1311,8 +1312,8 @@ void DrawDirtyBlocks() if (HasModalProgress()) { /* We are generating the world, so release our rights to the map and * painting while we are waiting a bit. */ - _modal_progress_paint_mutex->EndCritical(); - _modal_progress_work_mutex->EndCritical(); + _modal_progress_paint_mutex.unlock(); + _modal_progress_work_mutex.unlock(); /* Wait a while and update _realtime_tick so we are given the rights */ if (!IsFirstModalProgressLoop()) CSleep(MODAL_PROGRESS_REDRAW_TIMEOUT); @@ -1320,9 +1321,9 @@ void DrawDirtyBlocks() /* Modal progress thread may need blitter access while we are waiting for it. */ VideoDriver::GetInstance()->ReleaseBlitterLock(); - _modal_progress_paint_mutex->BeginCritical(); + _modal_progress_paint_mutex.lock(); VideoDriver::GetInstance()->AcquireBlitterLock(); - _modal_progress_work_mutex->BeginCritical(); + _modal_progress_work_mutex.lock(); /* When we ended with the modal progress, do not draw the blocks. * Simply let the next run do so, otherwise we would be loading diff --git a/src/gfx_func.h b/src/gfx_func.h index 7a0c02c146..4a037d24c0 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -75,7 +75,6 @@ void HandleKeypress(uint keycode, WChar key); void HandleTextInput(const char *str, bool marked = false, const char *caret = NULL, const char *insert_location = NULL, const char *replacement_end = NULL); void HandleCtrlChanged(); void HandleMouseEvents(); -void CSleep(int milliseconds); void UpdateWindows(); void DrawMouseCursor(); diff --git a/src/linkgraph/linkgraphjob.h b/src/linkgraph/linkgraphjob.h index 0849f63cf2..59b48801f3 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 "../core/dyn_arena_alloc.hpp" #include "linkgraph.h" #include diff --git a/src/linkgraph/linkgraphschedule.cpp b/src/linkgraph/linkgraphschedule.cpp index 0be9c54278..63a87484f6 100644 --- a/src/linkgraph/linkgraphschedule.cpp +++ b/src/linkgraph/linkgraphschedule.cpp @@ -142,13 +142,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) { if (job->IsJobAborted()) return; instance.handlers[i]->Run(*job); @@ -232,20 +230,17 @@ LinkGraphSchedule::~LinkGraphSchedule() LinkGraphJobGroup::LinkGraphJobGroup(constructor_token token, std::vector jobs) : jobs(std::move(jobs)) { } -void LinkGraphJobGroup::SpawnThread() { - ThreadObject *t = nullptr; - +void LinkGraphJobGroup::SpawnThread() +{ /** * Spawn a thread if possible and run the link graph job in the thread. If * that's not possible run the job right now in the current thread. */ - if (ThreadObject::New(&(LinkGraphJobGroup::Run), this, &t, "ottd:linkgraph")) { - this->thread.reset(t); + if (StartNewThread(&this->thread, "ottd:linkgraph", &(LinkGraphJobGroup::Run), this)) { for (auto &it : this->jobs) { it->SetJobGroup(this->shared_from_this()); } } else { - this->thread.reset(); /* 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. @@ -257,10 +252,11 @@ void LinkGraphJobGroup::SpawnThread() { } } -void LinkGraphJobGroup::JoinThread() { - if (!this->thread || this->joined_thread) return; - this->thread->Join(); - this->joined_thread = true; +void LinkGraphJobGroup::JoinThread() +{ + if (this->thread.joinable()) { + this->thread.join(); + } } /** diff --git a/src/linkgraph/linkgraphschedule.h b/src/linkgraph/linkgraphschedule.h index cebd974893..ab2ac201af 100644 --- a/src/linkgraph/linkgraphschedule.h +++ b/src/linkgraph/linkgraphschedule.h @@ -12,7 +12,7 @@ #ifndef LINKGRAPHSCHEDULE_H #define LINKGRAPHSCHEDULE_H -#include "../thread/thread.h" +#include "../thread.h" #include "linkgraph.h" #include @@ -55,7 +55,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(); @@ -85,8 +85,7 @@ class LinkGraphJobGroup : public std::enable_shared_from_this friend LinkGraphJob; private: - bool joined_thread = false; ///< True if thread has already been joined - std::unique_ptr thread; ///< Thread the job group is running in or NULL if it's running in the main thread. + std::thread thread; ///< Thread the job group is running in or NULL if it's running in the main thread. const std::vector jobs; ///< The set of jobs in this job set private: diff --git a/src/music/dmusic.cpp b/src/music/dmusic.cpp index 241ab191bf..bf8657ef45 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" @@ -30,6 +30,7 @@ #include #include #include +#include #include "../safeguards.h" @@ -138,11 +139,11 @@ 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. */ -static ThreadMutex *_thread_mutex = NULL; +static std::mutex _thread_mutex; /** The direct music object manages buffers and ports. */ static IDirectMusic *_music = NULL; @@ -596,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"); @@ -655,7 +656,7 @@ static void MidiThreadProc(void *) DEBUG(driver, 2, "DMusic thread: Starting playback"); { /* New scope to limit the time the mutex is locked. */ - ThreadMutexLocker lock(_thread_mutex); + std::lock_guard lock(_thread_mutex); current_file.MoveFrom(_playback.next_file); std::swap(_playback.next_segment, current_segment); @@ -1167,10 +1168,8 @@ const char *MusicDriver_DMusic::Start(const char * const *parm) /* Create playback thread and synchronization primitives. */ _thread_event = CreateEvent(NULL, FALSE, FALSE, NULL); if (_thread_event == NULL) return "Can't create thread shutdown event"; - _thread_mutex = ThreadMutex::New(); - if (_thread_mutex == NULL) return "Can't create thread mutex"; - 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; } @@ -1184,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. */ @@ -1223,7 +1222,6 @@ void MusicDriver_DMusic::Stop() } CloseHandle(_thread_event); - delete _thread_mutex; CoUninitialize(); } @@ -1231,7 +1229,7 @@ void MusicDriver_DMusic::Stop() void MusicDriver_DMusic::PlaySong(const MusicSongInfo &song) { - ThreadMutexLocker lock(_thread_mutex); + std::lock_guard lock(_thread_mutex); if (!_playback.next_file.LoadSong(song)) return; diff --git a/src/music/extmidi.cpp b/src/music/extmidi.cpp index 9d07761b73..f7fc454dff 100644 --- a/src/music/extmidi.cpp +++ b/src/music/extmidi.cpp @@ -18,6 +18,7 @@ #include "../gfx_func.h" #include "extmidi.h" #include "../base_media_base.h" +#include "../thread.h" #include "midifile.hpp" #include #include diff --git a/src/network/core/tcp.h b/src/network/core/tcp.h index 243ec042d2..f9e1e00cbd 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 d76042821a..d923688b15 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(param)->Connect(); + param->Connect(); } /** diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 2cb588972f..6d0cc28e9e 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -30,6 +30,7 @@ #include "network_base.h" #include "network_client.h" #include "../core/backup_type.hpp" +#include "../thread.h" #include "table/strings.h" diff --git a/src/network/network_gamelist.cpp b/src/network/network_gamelist.cpp index 6c65c52c98..e9fc5f943e 100644 --- a/src/network/network_gamelist.cpp +++ b/src/network/network_gamelist.cpp @@ -15,19 +15,17 @@ #include "../stdafx.h" #include "../debug.h" #include "../window_func.h" -#include "../thread/thread.h" #include "network_internal.h" #include "network_udp.h" #include "network_gamelist.h" +#include #include "../safeguards.h" NetworkGameList *_network_game_list = NULL; -/** Mutex for handling delayed insertion/querying of servers. */ -static ThreadMutex *_network_game_list_mutex = ThreadMutex::New(); /** The games to insert when the GUI thread has time for us. */ -static NetworkGameList *_network_game_delayed_insertion_list = NULL; +static std::atomic _network_game_delayed_insertion_list(NULL); /** * Add a new item to the linked gamelist, but do it delayed in the next tick @@ -36,19 +34,17 @@ static NetworkGameList *_network_game_delayed_insertion_list = NULL; */ void NetworkGameListAddItemDelayed(NetworkGameList *item) { - _network_game_list_mutex->BeginCritical(); - item->next = _network_game_delayed_insertion_list; - _network_game_delayed_insertion_list = item; - _network_game_list_mutex->EndCritical(); + item->next = _network_game_delayed_insertion_list.load(std::memory_order_relaxed); + while (!_network_game_delayed_insertion_list.compare_exchange_weak(item->next, item, std::memory_order_acq_rel)) {} } /** Perform the delayed (thread safe) insertion into the game list */ static void NetworkGameListHandleDelayedInsert() { - _network_game_list_mutex->BeginCritical(); - while (_network_game_delayed_insertion_list != NULL) { - NetworkGameList *ins_item = _network_game_delayed_insertion_list; - _network_game_delayed_insertion_list = ins_item->next; + while (true) { + NetworkGameList *ins_item = _network_game_delayed_insertion_list.load(std::memory_order_relaxed); + while (ins_item != NULL && !_network_game_delayed_insertion_list.compare_exchange_weak(ins_item, ins_item->next, std::memory_order_acq_rel)) {} + if (ins_item == NULL) break; // No item left. NetworkGameList *item = NetworkGameListAddItem(ins_item->address); @@ -66,7 +62,6 @@ static void NetworkGameListHandleDelayedInsert() } free(ins_item); } - _network_game_list_mutex->EndCritical(); } /** diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 194aae4a33..5091ec6b0e 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -30,6 +30,8 @@ #include "../core/pool_func.hpp" #include "../core/random_func.hpp" #include "../rev.h" +#include +#include #include "../safeguards.h" @@ -58,7 +60,8 @@ struct PacketWriter : SaveFilter { Packet *current; ///< The packet we're currently writing to. size_t total_size; ///< Total size of the compressed savegame. Packet *packets; ///< Packet queue of the savegame; send these "slowly" to the client. - ThreadMutex *mutex; ///< Mutex for making threaded saving safe. + std::mutex mutex; ///< Mutex for making threaded saving safe. + std::condition_variable exit_sig; ///< Signal for threaded destruction of this packet writer. /** * Create the packet writer. @@ -66,17 +69,14 @@ struct PacketWriter : SaveFilter { */ PacketWriter(ServerNetworkGameSocketHandler *cs) : SaveFilter(NULL), cs(cs), current(NULL), total_size(0), packets(NULL) { - this->mutex = ThreadMutex::New(); } /** Make sure everything is cleaned up. */ ~PacketWriter() { - if (this->mutex != NULL) this->mutex->BeginCritical(); + std::unique_lock lock(this->mutex); - if (this->cs != NULL && this->mutex != NULL) { - this->mutex->WaitForSignal(); - } + if (this->cs != NULL) this->exit_sig.wait(lock); /* This must all wait until the Destroy function is called. */ @@ -87,11 +87,6 @@ struct PacketWriter : SaveFilter { } delete this->current; - - if (this->mutex != NULL) this->mutex->EndCritical(); - - delete this->mutex; - this->mutex = NULL; } /** @@ -106,13 +101,12 @@ struct PacketWriter : SaveFilter { */ void Destroy() { - if (this->mutex != NULL) this->mutex->BeginCritical(); + std::unique_lock lock(this->mutex); this->cs = NULL; - if (this->mutex != NULL) this->mutex->SendSignal(); - - if (this->mutex != NULL) this->mutex->EndCritical(); + this->exit_sig.notify_all(); + lock.unlock(); /* Make sure the saving is completely cancelled. Yes, * we need to handle the save finish as well as the @@ -138,14 +132,12 @@ struct PacketWriter : SaveFilter { */ Packet *PopPacket() { - if (this->mutex != NULL) this->mutex->BeginCritical(); + std::lock_guard lock(this->mutex); Packet *p = this->packets; this->packets = p->next; p->next = NULL; - if (this->mutex != NULL) this->mutex->EndCritical(); - return p; } @@ -170,7 +162,7 @@ struct PacketWriter : SaveFilter { if (this->current == NULL) this->current = new Packet(PACKET_SERVER_MAP_DATA); - if (this->mutex != NULL) this->mutex->BeginCritical(); + std::lock_guard lock(this->mutex); byte *bufe = buf + size; while (buf != bufe) { @@ -185,8 +177,6 @@ struct PacketWriter : SaveFilter { } } - if (this->mutex != NULL) this->mutex->EndCritical(); - this->total_size += size; } @@ -195,7 +185,7 @@ struct PacketWriter : SaveFilter { /* We want to abort the saving when the socket is closed. */ if (this->cs == NULL) SlError(STR_NETWORK_ERROR_LOSTCONNECTION); - if (this->mutex != NULL) this->mutex->BeginCritical(); + std::lock_guard lock(this->mutex); /* Make sure the last packet is flushed. */ this->AppendQueue(); @@ -208,8 +198,6 @@ struct PacketWriter : SaveFilter { Packet *p = new Packet(PACKET_SERVER_MAP_SIZE); p->Send_uint32((uint32)this->total_size); this->cs->NetworkTCPSocketHandler::SendPacket(p); - - if (this->mutex != NULL) this->mutex->EndCritical(); } }; @@ -1681,7 +1669,7 @@ static void NetworkAutoCleanCompanies() /* Is the company empty for autoclean_unprotected-months, and is there no protection? */ if (_settings_client.network.autoclean_unprotected != 0 && _network_company_states[c->index].months_empty > _settings_client.network.autoclean_unprotected && StrEmpty(_network_company_states[c->index].password)) { /* Shut the company down */ - DoCommandP(0, CCA_DELETE | c->index << 16, CRR_AUTOCLEAN, CMD_COMPANY_CTRL); + DoCommandP(0, CCA_DELETE | c->index << 16 | CRR_AUTOCLEAN << 24, 0, CMD_COMPANY_CTRL); IConsolePrintF(CC_DEFAULT, "Auto-cleaned company #%d with no password", c->index + 1); } /* Is the company empty for autoclean_protected-months, and there is a protection? */ @@ -1695,7 +1683,7 @@ static void NetworkAutoCleanCompanies() /* Is the company empty for autoclean_novehicles-months, and has no vehicles? */ if (_settings_client.network.autoclean_novehicles != 0 && _network_company_states[c->index].months_empty > _settings_client.network.autoclean_novehicles && vehicles_in_company[c->index] == 0) { /* Shut the company down */ - DoCommandP(0, CCA_DELETE | c->index << 16, CRR_AUTOCLEAN, CMD_COMPANY_CTRL); + DoCommandP(0, CCA_DELETE | c->index << 16 | CRR_AUTOCLEAN << 24, 0, CMD_COMPANY_CTRL); IConsolePrintF(CC_DEFAULT, "Auto-cleaned company #%d with no vehicles", c->index + 1); } } else { diff --git a/src/network/network_server.h b/src/network/network_server.h index fe98128b82..e985bd535c 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 1e82bd5928..d323dfd156 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -24,11 +24,12 @@ #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" #include "table/strings.h" +#include #include "core/udp.h" @@ -40,7 +41,7 @@ extern const uint8 _out_of_band_grf_md5[16] { 0x00, 0xB0, 0xC0, 0xDE, 0x00, 0x00 static const uint32 FIND_SERVER_EXTENDED_TOKEN = 0x2A49582A; /** Mutex for all out threaded udp resolution and such. */ -static ThreadMutex *_network_udp_mutex = ThreadMutex::New(); +static std::mutex _network_udp_mutex; /** Session key to register ourselves to the master server */ static uint64 _session_key = 0; @@ -53,22 +54,6 @@ 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) - { - } -}; - static Packet PrepareUdpClientFindServerPacket() { Packet p(PACKET_UDP_CLIENT_FIND_SERVER); @@ -84,33 +69,21 @@ static Packet PrepareUdpClientFindServerPacket() * @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(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); - if (needs_mutex) _network_udp_mutex->BeginCritical(); + std::unique_lock lock(_network_udp_mutex, std::defer_lock); + if (needs_mutex) lock.lock(); /* Init the packet */ Packet p = PrepareUdpClientFindServerPacket(); - if (_udp_client_socket != NULL) _udp_client_socket->SendPacket(&p, address); - if (needs_mutex) _network_udp_mutex->EndCritical(); -} - -/** - * 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); } /** @@ -120,9 +93,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); } } @@ -510,7 +482,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); } } } @@ -616,9 +588,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"); @@ -631,9 +602,8 @@ static void NetworkUDPRemoveAdvertiseThread(void *pntr) p.Send_uint8 (NETWORK_MASTER_SERVER_VERSION); p.Send_uint16(_settings_client.network.server_port); - _network_udp_mutex->BeginCritical(); + std::lock_guard lock(_network_udp_mutex); if (_udp_master_socket != NULL) _udp_master_socket->SendPacket(&p, &out_addr, true); - _network_udp_mutex->EndCritical(); } /** @@ -645,16 +615,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); @@ -685,9 +654,8 @@ static void NetworkUDPAdvertiseThread(void *pntr) p.Send_uint16(_settings_client.network.server_port); p.Send_uint64(_session_key); - _network_udp_mutex->BeginCritical(); + std::lock_guard lock(_network_udp_mutex); if (_udp_master_socket != NULL) _udp_master_socket->SendPacket(&p, &out_addr, true); - _network_udp_mutex->EndCritical(); } /** @@ -728,8 +696,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(); } } @@ -742,7 +710,7 @@ void NetworkUDPInitialize() DEBUG(net, 1, "[udp] initializing listeners"); assert(_udp_client_socket == NULL && _udp_server_socket == NULL && _udp_master_socket == NULL); - _network_udp_mutex->BeginCritical(); + std::lock_guard lock(_network_udp_mutex); _udp_client_socket = new ClientNetworkUDPSocketHandler(); @@ -756,13 +724,12 @@ void NetworkUDPInitialize() _network_udp_server = false; _network_udp_broadcast = 0; - _network_udp_mutex->EndCritical(); } /** Close all UDP related stuff. */ void NetworkUDPClose() { - _network_udp_mutex->BeginCritical(); + std::lock_guard lock(_network_udp_mutex); _udp_server_socket->Close(); _udp_master_socket->Close(); _udp_client_socket->Close(); @@ -772,7 +739,6 @@ void NetworkUDPClose() _udp_client_socket = NULL; _udp_server_socket = NULL; _udp_master_socket = NULL; - _network_udp_mutex->EndCritical(); _network_udp_server = false; _network_udp_broadcast = 0; @@ -782,7 +748,7 @@ void NetworkUDPClose() /** Receive the UDP packets. */ void NetworkBackgroundUDPLoop() { - _network_udp_mutex->BeginCritical(); + std::lock_guard lock(_network_udp_mutex); if (_network_udp_server) { _udp_server_socket->ReceivePackets(); @@ -791,6 +757,4 @@ void NetworkBackgroundUDPLoop() _udp_client_socket->ReceivePackets(); if (_network_udp_broadcast > 0) _network_udp_broadcast--; } - - _network_udp_mutex->EndCritical(); } diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp index e270634d37..3e61b1cd7a 100644 --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -21,6 +21,8 @@ #include "video/video_driver.hpp" #include "strings_func.h" #include "textfile_gui.h" +#include "thread.h" +#include "newgrf_config.h" #include "fileio_func.h" #include "fios.h" @@ -684,18 +686,18 @@ bool GRFFileScanner::AddFile(const char *filename, size_t basepath_length, const this->num_scanned++; if (this->next_update <= _realtime_tick) { - _modal_progress_work_mutex->EndCritical(); - _modal_progress_paint_mutex->BeginCritical(); + _modal_progress_work_mutex.unlock(); + _modal_progress_paint_mutex.lock(); const char *name = NULL; if (c->name != NULL) name = GetGRFStringFromGRFText(c->name->text); if (name == NULL) name = c->filename; UpdateNewGRFScanStatus(this->num_scanned, name); - _modal_progress_work_mutex->BeginCritical(); - _modal_progress_paint_mutex->EndCritical(); + _modal_progress_work_mutex.lock(); + _modal_progress_paint_mutex.unlock(); - this->next_update = _realtime_tick + 200; + this->next_update = _realtime_tick + MODAL_PROGRESS_REDRAW_TIMEOUT; } if (!added) { @@ -725,9 +727,9 @@ 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) { - _modal_progress_work_mutex->BeginCritical(); + std::unique_lock lock_work(_modal_progress_work_mutex); ClearGRFConfigList(&_all_grfs); TarScanner::DoScan(TarScanner::NEWGRF); @@ -762,18 +764,17 @@ void DoScanNewGRFFiles(void *callback) NetworkAfterNewGRFScan(); } - _modal_progress_work_mutex->EndCritical(); - _modal_progress_paint_mutex->BeginCritical(); + lock_work.unlock(); + std::lock_guard lock_paint(_modal_progress_paint_mutex); /* 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); MarkWholeScreenDirty(); - _modal_progress_paint_mutex->EndCritical(); } /** @@ -787,12 +788,12 @@ void ScanNewGRFFiles(NewGRFScanCallback *callback) /* Only then can we really start, especially by marking the whole screen dirty. Get those other windows hidden!. */ MarkWholeScreenDirty(); - if (!VideoDriver::GetInstance()->HasGUI() || !ThreadObject::New(&DoScanNewGRFFiles, callback, NULL, "ottd:newgrf-scan")) { - _modal_progress_work_mutex->EndCritical(); - _modal_progress_paint_mutex->EndCritical(); + 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); - _modal_progress_paint_mutex->BeginCritical(); - _modal_progress_work_mutex->BeginCritical(); + _modal_progress_paint_mutex.lock(); + _modal_progress_work_mutex.lock(); } else { UpdateNewGRFScanStatus(0, NULL); } diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index 4d7750491f..3e9ef8be60 100644 --- a/src/object_cmd.cpp +++ b/src/object_cmd.cpp @@ -834,9 +834,10 @@ static void ChangeTileOwner_Object(TileIndex tile, Owner old_owner, Owner new_ow bool do_clear = false; - if (IsObjectType(tile, OBJECT_OWNED_LAND) && new_owner != INVALID_OWNER) { + ObjectType type = GetObjectType(tile); + if ((type == OBJECT_OWNED_LAND || type >= NEW_OBJECT_OFFSET) && new_owner != INVALID_OWNER) { SetTileOwner(tile, new_owner); - } else if (IsObjectType(tile, OBJECT_STATUE)) { + } else if (type == OBJECT_STATUE) { Town *t = Object::GetByTile(tile)->town; ClrBit(t->statues, old_owner); if (new_owner != INVALID_OWNER && !HasBit(t->statues, new_owner)) { diff --git a/src/openttd.cpp b/src/openttd.cpp index af851b0d4c..8b5621c9ad 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -68,7 +68,7 @@ #include "programmable_signals.h" #include "smallmap_gui.h" #include "viewport_func.h" -#include "thread/thread.h" +#include "thread.h" #include "bridge_signal_map.h" #include "zoning.h" #include "cargopacket.h" @@ -77,6 +77,7 @@ #include "tracerestrict.h" #include +#include #include "safeguards.h" @@ -613,6 +614,9 @@ int openttd_main(int argc, char *argv[]) extern bool _dedicated_forks; _dedicated_forks = false; + std::unique_lock modal_work_lock(_modal_progress_work_mutex, std::defer_lock); + std::unique_lock modal_paint_lock(_modal_progress_paint_mutex, std::defer_lock); + _game_mode = GM_MENU; _switch_mode = SM_MENU; _config_file = NULL; @@ -897,8 +901,14 @@ int openttd_main(int argc, char *argv[]) free(musicdriver); /* Take our initial lock on whatever we might want to do! */ - _modal_progress_paint_mutex->BeginCritical(); - _modal_progress_work_mutex->BeginCritical(); + try { + modal_work_lock.lock(); + modal_paint_lock.lock(); + } catch (const std::system_error&) { + /* If there is some error we assume that threads aren't usable on the system we run. */ + extern bool _use_threaded_modal_progress; // From progress.cpp + _use_threaded_modal_progress = false; + } GenerateWorld(GWM_EMPTY, 64, 64); // Make the viewport initialization happy WaitTillGeneratedWorld(); @@ -916,6 +926,7 @@ int openttd_main(int argc, char *argv[]) CrashLog::MainThreadExitCheckPendingCrashlog(); 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/macosx/macos.mm b/src/os/macosx/macos.mm index 7fb71fe9ee..ae9d86ee8b 100644 --- a/src/os/macosx/macos.mm +++ b/src/os/macosx/macos.mm @@ -206,23 +206,6 @@ bool GetClipboardContents(char *buffer, const char *last) } #endif -uint GetCPUCoreCount() -{ - uint count = 1; -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) - if (MacOSVersionIsAtLeast(10, 5, 0)) { - count = (uint)[ [ NSProcessInfo processInfo ] activeProcessorCount ]; - } else -#endif - { -#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) - count = MPProcessorsScheduled(); -#endif - } - - return count; -} - /** * Check if a font is a monospace font. * @param name Name of the font. diff --git a/src/os/os2/os2.cpp b/src/os/os2/os2.cpp index 7b34f528a6..a77f6c325b 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" @@ -204,25 +205,22 @@ bool GetClipboardContents(char *buffer, const char *last) } -void CSleep(int milliseconds) -{ -#ifndef __INNOTEK_LIBC__ - delay(milliseconds); -#else - usleep(milliseconds * 1000); -#endif -} - const char *FS2OTTD(const char *name) {return name;} const char *OTTD2FS(const char *name) {return name;} -uint GetCPUCoreCount() -{ - return 1; -} - void OSOpenBrowser(const char *url) { // stub only DEBUG(misc, 0, "Failed to open url: %s", url); } + +void SetCurrentThreadName(const char *) +{ +} + +int GetCurrentThreadName(char *str, const char *last) { return 0; } + +void SetSelfAsMainThread() { } + +bool IsMainThread() { return false; } +bool IsNonMainThread() { return false; } diff --git a/src/os/unix/unix.cpp b/src/os/unix/unix.cpp index 2f982dea06..f9a2cd0028 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 @@ -43,11 +44,17 @@ #include #endif +#ifndef NO_THREADS +#include +#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 - #endif +# include +# endif + +# include "../macosx/macos.h" #endif #include "../../safeguards.h" @@ -266,44 +273,7 @@ bool GetClipboardContents(char *buffer, const char *last) #endif -/* multi os compatible sleep function */ - -void CSleep(int milliseconds) -{ - usleep(milliseconds * 1000); -} - - #ifndef __APPLE__ -uint GetCPUCoreCount() -{ - uint count = 1; -#ifdef HAS_SYSCTL - int ncpu = 0; - size_t len = sizeof(ncpu); - -#ifdef OPENBSD - int name[2]; - name[0] = CTL_HW; - name[1] = HW_NCPU; - if (sysctl(name, 2, &ncpu, &len, NULL, 0) < 0) { - ncpu = 0; - } -#else - if (sysctlbyname("hw.availcpu", &ncpu, &len, NULL, 0) < 0) { - sysctlbyname("hw.ncpu", &ncpu, &len, NULL, 0); - } -#endif /* #ifdef OPENBSD */ - - if (ncpu > 0) count = ncpu; -#elif defined(_SC_NPROCESSORS_ONLN) - long res = sysconf(_SC_NPROCESSORS_ONLN); - if (res > 0) count = res; -#endif - - return count; -} - void OSOpenBrowser(const char *url) { pid_t child_pid = fork(); @@ -317,4 +287,56 @@ void OSOpenBrowser(const char *url) DEBUG(misc, 0, "Failed to open url: %s", url); exit(0); } +#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__) */ +} + +int GetCurrentThreadName(char *str, const char *last) +{ +#if !defined(NO_THREADS) && defined(__GLIBC__) +#if __GLIBC_PREREQ(2, 12) + char buffer[16]; + int result = pthread_getname_np(pthread_self(), buffer, sizeof(buffer)); + if (result == 0) { + return seprintf(str, last, "%s", buffer); + } +#endif +#endif + return 0; +} + +static pthread_t main_thread; + +void SetSelfAsMainThread() +{ +#if !defined(NO_THREADS) + main_thread = pthread_self(); #endif +} + +bool IsMainThread() +{ +#if !defined(NO_THREADS) + return main_thread == pthread_self(); +#else + return true; +#endif +} + +bool IsNonMainThread() +{ +#if !defined(NO_THREADS) + return main_thread != pthread_self(); +#else + return false; +#endif +} diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index db2636e281..6fe8d6ebfa 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -30,6 +30,7 @@ #include #include #include "../../language.h" +#include "../../thread.h" #include "../../safeguards.h" @@ -81,7 +82,7 @@ void ShowOSErrorBox(const char *buf, bool system) { _in_event_loop_post_crash = true; MyShowCursor(true); - MessageBox(GetActiveWindow(), OTTD2FS(buf), _T("Error!"), MB_ICONSTOP); + MessageBox(GetActiveWindow(), OTTD2FS(buf), _T("Error!"), MB_ICONSTOP | MB_TASKMODAL); } void OSOpenBrowser(const char *url) @@ -546,12 +547,6 @@ bool GetClipboardContents(char *buffer, const char *last) } -void CSleep(int milliseconds) -{ - Sleep(milliseconds); -} - - /** * Convert to OpenTTD's encoding from that of the local environment. * When the project is built in UNICODE, the system codepage is irrelevant and @@ -733,14 +728,6 @@ const char *GetCurrentLocale(const char *) return retbuf; } -uint GetCPUCoreCount() -{ - SYSTEM_INFO info; - - GetSystemInfo(&info); - return info.dwNumberOfProcessors; -} - static WCHAR _cur_iso_locale[16] = L""; @@ -805,6 +792,42 @@ int OTTDStringCompare(const char *s1, const char *s2) return CompareString(MAKELCID(_current_language->winlangid, SORT_DEFAULT), NORM_IGNORECASE, s1_buf, -1, s2_buf, -1); } +static DWORD main_thread_id; + +void SetSelfAsMainThread() +{ + main_thread_id = GetCurrentThreadId(); +} + +bool IsMainThread() +{ + return main_thread_id == GetCurrentThreadId(); +} + +bool IsNonMainThread() +{ + return main_thread_id != GetCurrentThreadId(); +} + +static std::map _thread_name_map; +static std::mutex _thread_name_map_mutex; + +static void Win32SetThreadName(uint id, const char *name) +{ + std::lock_guard lock(_thread_name_map_mutex); + _thread_name_map[id] = name; +} + +int GetCurrentThreadName(char *str, const char *last) +{ + std::lock_guard lock(_thread_name_map_mutex); + auto iter = _thread_name_map.find(GetCurrentThreadId()); + if (iter != _thread_name_map.end()) { + return seprintf(str, last, "%s", iter->second.c_str()); + } + return 0; +} + #ifdef _MSC_VER /* Based on code from MSDN: https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ const DWORD MS_VC_EXCEPTION = 0x406D1388; @@ -819,12 +842,14 @@ PACK_N(struct THREADNAME_INFO { /** * Signal thread name to any attached debuggers. */ -void SetWin32ThreadName(DWORD dwThreadID, const char* threadName) +void SetCurrentThreadName(const char *threadName) { + Win32SetThreadName(GetCurrentThreadId(), threadName); + THREADNAME_INFO info; info.dwType = 0x1000; info.szName = threadName; - info.dwThreadID = dwThreadID; + info.dwThreadID = -1; info.dwFlags = 0; #pragma warning(push) @@ -835,4 +860,9 @@ void SetWin32ThreadName(DWORD dwThreadID, const char* threadName) } #pragma warning(pop) } +#else +void SetCurrentThreadName(const char *) +{ + Win32SetThreadName(GetCurrentThreadId(), threadName); +} #endif diff --git a/src/os/windows/win32.h b/src/os/windows/win32.h index 4f813c4a6f..23e216762c 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/progress.cpp b/src/progress.cpp index b498be109b..923f1ef83f 100644 --- a/src/progress.cpp +++ b/src/progress.cpp @@ -10,17 +10,19 @@ /** @file progress.cpp Functions for modal progress windows. */ #include "stdafx.h" -#include "thread/thread.h" +#include "progress.h" #include "safeguards.h" /** Are we in a modal progress or not? */ bool _in_modal_progress = false; bool _first_in_modal_loop = false; +/** Threading usable for modal progress? */ +bool _use_threaded_modal_progress = true; /** Rights for the performing work. */ -ThreadMutex *_modal_progress_work_mutex = ThreadMutex::New(); +std::mutex _modal_progress_work_mutex; /** Rights for the painting. */ -ThreadMutex *_modal_progress_paint_mutex = ThreadMutex::New(); +std::mutex _modal_progress_paint_mutex; /** * Set the modal progress state. diff --git a/src/progress.h b/src/progress.h index eec369b23c..2a9d73b038 100644 --- a/src/progress.h +++ b/src/progress.h @@ -12,7 +12,7 @@ #ifndef PROGRESS_H #define PROGRESS_H -#include "thread/thread.h" +#include static const uint MODAL_PROGRESS_REDRAW_TIMEOUT = 200; ///< Timeout between redraws @@ -26,10 +26,20 @@ static inline bool HasModalProgress() return _in_modal_progress; } +/** + * Check if we can use a thread for modal progress. + * @return Threading usable? + */ +static inline bool UseThreadedModelProgress() +{ + extern bool _use_threaded_modal_progress; + return _use_threaded_modal_progress; +} + bool IsFirstModalProgressLoop(); void SetModalProgress(bool state); -extern class ThreadMutex *_modal_progress_work_mutex; -extern class ThreadMutex *_modal_progress_paint_mutex; +extern std::mutex _modal_progress_work_mutex; +extern std::mutex _modal_progress_paint_mutex; #endif /* PROGRESS_H */ diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 40a15834a1..7bae3154fb 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -26,7 +26,7 @@ #include "../debug.h" #include "../station_base.h" #include "../dock_base.h" -#include "../thread/thread.h" +#include "../thread.h" #include "../town.h" #include "../network/network.h" #include "../window_func.h" @@ -46,6 +46,7 @@ #include "../string_func_extra.h" #include "../fios.h" #include "../error.h" +#include #include "../tbtr_template_vehicle.h" @@ -427,9 +428,9 @@ void NORETURN CDECL SlErrorCorruptFmt(const char *format, ...) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, str, true); } -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 +typedef void (*AsyncSaveFinishProc)(); ///< Callback for when the savegame loading is finished. +static std::atomic _async_save_finish; ///< Callback to call when the savegame loading is finished. +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. @@ -438,9 +439,9 @@ static ThreadObject *_save_thread; ///< The thread we're usin static void SetAsyncSaveFinish(AsyncSaveFinishProc proc) { if (_exit_game) return; - while (_async_save_finish != NULL) CSleep(10); + while (_async_save_finish.load(std::memory_order_acquire) != NULL) CSleep(10); - _async_save_finish = proc; + _async_save_finish.store(proc, std::memory_order_release); } /** @@ -448,16 +449,13 @@ static void SetAsyncSaveFinish(AsyncSaveFinishProc proc) */ void ProcessAsyncSaveFinish() { - if (_async_save_finish == NULL) return; - - _async_save_finish(); + AsyncSaveFinishProc proc = _async_save_finish.exchange(NULL, std::memory_order_acq_rel); + if (proc == NULL) return; - _async_save_finish = NULL; + proc(); - if (_save_thread != NULL) { - _save_thread->Join(); - delete _save_thread; - _save_thread = NULL; + if (_save_thread.joinable()) { + _save_thread.join(); } } @@ -2759,19 +2757,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(); @@ -2799,7 +2789,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/ship_cmd.cpp b/src/ship_cmd.cpp index da6e6fdd66..e79ec10734 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -539,8 +539,6 @@ static inline TrackBits GetAvailShipTracks(TileIndex tile, DiagDirection dir, Tr { TrackBits tracks = GetTileShipTrackStatus(tile) & DiagdirReachesTracks(dir); - if (_settings_game.pf.forbid_90_deg) tracks &= ~TrackCrossesTracks(TrackdirToTrack(trackdir)); - return tracks; } diff --git a/src/sound/win32_s.cpp b/src/sound/win32_s.cpp index c9c1a8afdc..493ed7fc17 100644 --- a/src/sound/win32_s.cpp +++ b/src/sound/win32_s.cpp @@ -20,6 +20,7 @@ #include #include #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/station_cmd.cpp b/src/station_cmd.cpp index b3fa1499db..c77d0c7353 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2896,21 +2896,29 @@ bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrack bool snow_desert; switch (*ground) { case SPR_RAIL_TRACK_X: + case SPR_MONO_TRACK_X: + case SPR_MGLV_TRACK_X: snow_desert = false; *overlay_offset = RTO_X; break; case SPR_RAIL_TRACK_Y: + case SPR_MONO_TRACK_Y: + case SPR_MGLV_TRACK_Y: snow_desert = false; *overlay_offset = RTO_Y; break; case SPR_RAIL_TRACK_X_SNOW: + case SPR_MONO_TRACK_X_SNOW: + case SPR_MGLV_TRACK_X_SNOW: snow_desert = true; *overlay_offset = RTO_X; break; case SPR_RAIL_TRACK_Y_SNOW: + case SPR_MONO_TRACK_Y_SNOW: + case SPR_MGLV_TRACK_Y_SNOW: snow_desert = true; *overlay_offset = RTO_Y; break; diff --git a/src/table/sprites.h b/src/table/sprites.h index 09d772c3ad..1f4f8caf84 100644 --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -435,8 +435,11 @@ static const SpriteID SPR_MONO_SINGLE_SOUTH = 1090; static const SpriteID SPR_MONO_SINGLE_EAST = 1091; static const SpriteID SPR_MONO_SINGLE_WEST = 1092; static const SpriteID SPR_MONO_TRACK_Y = 1093; +static const SpriteID SPR_MONO_TRACK_X = 1094; static const SpriteID SPR_MONO_TRACK_BASE = 1100; static const SpriteID SPR_MONO_TRACK_N_S = 1117; +static const SpriteID SPR_MONO_TRACK_Y_SNOW = 1119; +static const SpriteID SPR_MONO_TRACK_X_SNOW = 1120; static const SpriteID SPR_MGLV_SINGLE_X = 1169; static const SpriteID SPR_MGLV_SINGLE_Y = 1170; static const SpriteID SPR_MGLV_SINGLE_NORTH = 1171; @@ -444,7 +447,10 @@ static const SpriteID SPR_MGLV_SINGLE_SOUTH = 1172; static const SpriteID SPR_MGLV_SINGLE_EAST = 1173; static const SpriteID SPR_MGLV_SINGLE_WEST = 1174; static const SpriteID SPR_MGLV_TRACK_Y = 1175; +static const SpriteID SPR_MGLV_TRACK_X = 1176; static const SpriteID SPR_MGLV_TRACK_BASE = 1182; +static const SpriteID SPR_MGLV_TRACK_Y_SNOW = 1184; +static const SpriteID SPR_MGLV_TRACK_X_SNOW = 1185; static const SpriteID SPR_MGLV_TRACK_N_S = 1199; static const SpriteID SPR_WAYPOINT_X_1 = SPR_OPENTTD_BASE + 78; static const SpriteID SPR_WAYPOINT_X_2 = SPR_OPENTTD_BASE + 79; diff --git a/src/thread.h b/src/thread.h new file mode 100644 index 0000000000..6111cf1221 --- /dev/null +++ b/src/thread.h @@ -0,0 +1,104 @@ +/* $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 . + */ + +/** @file thread.h Base of all threads. */ + +#ifndef THREAD_H +#define THREAD_H + +#include "debug.h" +#include +#include + +/** Signal used for signalling we knowingly want to end the thread. */ +class OTTDThreadExitSignal { }; + + +/** + * Sleep on the current thread for a defined time. + * @param milliseconds Time to sleep for in milliseconds. + */ +inline void CSleep(int milliseconds) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); +} + +/** + * Name the thread this function is called on for the debugger. + * @param name Name to set for the thread.. + */ +void SetCurrentThreadName(const char *name); + +/** + * Get the name of the current thread, if any. + * @param str The start of the buffer. + * @param last The last char of the buffer. + * @return Number of chars written to str. + */ +int GetCurrentThreadName(char *str, const char *last); + +/** + * Set the current thread as the "main" thread + */ +void SetSelfAsMainThread(); + +/** + * @return true if the current thread definitely the "main" thread. If in doubt returns false. + */ +bool IsMainThread(); + +/** + * @return true if the current thread definitely a "non-main" thread. If in doubt returns false. + */ +bool IsNonMainThread(); + + +/** + * 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 +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(_Fx), std::forward(_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 ecfd655fb6..0000000000 --- a/src/thread/thread.h +++ /dev/null @@ -1,149 +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 . - */ - -/** @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); -}; - -/** - * Cross-platform Mutex - */ -class ThreadMutex { -public: - /** - * Create a new mutex. - */ - static ThreadMutex *New(); - - /** - * Virtual Destructor to avoid compiler warnings. - */ - virtual ~ThreadMutex() {}; - - /** - * Begin the critical section - * @param allow_recursive Whether recursive locking is intentional. - * If false, NOT_REACHED() will be called when the mutex is already locked - * by the current thread. - */ - virtual void BeginCritical(bool allow_recursive = false) = 0; - - /** - * End of the critical section - * @param allow_recursive Whether recursive unlocking is intentional. - * If false, NOT_REACHED() will be called when the mutex was locked more - * than once by the current thread. - */ - virtual void EndCritical(bool allow_recursive = false) = 0; - - /** - * Wait for a signal to be send. - * @pre You must be in the critical section. - * @note While waiting the critical section is left. - * @post You will be in the critical section. - */ - virtual void WaitForSignal() = 0; - - /** - * Send a signal and wake the 'thread' that was waiting for it. - */ - virtual void SendSignal() = 0; -}; - -/** - * Simple mutex locker to keep a mutex locked until the locker goes out of scope. - */ -class ThreadMutexLocker { -public: - /** - * Lock the mutex and keep it locked for the life time of this object. - * @param mutex Mutex to be locked. - */ - ThreadMutexLocker(ThreadMutex *mutex) : mutex(mutex) { mutex->BeginCritical(); } - - /** - * Unlock the mutex. - */ - ~ThreadMutexLocker() { this->mutex->EndCritical(); } - -private: - ThreadMutexLocker(const ThreadMutexLocker &) { NOT_REACHED(); } - ThreadMutexLocker &operator=(const ThreadMutexLocker &) { NOT_REACHED(); return *this; } - ThreadMutex *mutex; -}; - -/** - * Get number of processor cores in the system, including HyperThreading or similar. - * @return Total number of processor cores. - */ -uint GetCPUCoreCount(); - -/** - * Set the current thread as the "main" thread - */ -void SetSelfAsMainThread(); - -/** - * @return true if the current thread definitely the "main" thread. If in doubt returns false. - */ -bool IsMainThread(); - -/** - * @return true if the current thread definitely a "non-main" thread. If in doubt returns false. - */ -bool IsNonMainThread(); - -/** - * Get the name of the current thread, if any. - * @param str The start of the buffer. - * @param last The last char of the buffer. - * @return Number of chars written to str. - */ -int GetThreadName(char *str, const char *last); - -#endif /* THREAD_H */ diff --git a/src/thread/thread_none.cpp b/src/thread/thread_none.cpp deleted file mode 100644 index 175cf32015..0000000000 --- a/src/thread/thread_none.cpp +++ /dev/null @@ -1,42 +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 . - */ - -/** @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() {} -}; - -/* static */ ThreadMutex *ThreadMutex::New() -{ - return new ThreadMutex_None(); -} - -void SetSelfAsMainThread() { } - -bool IsMainThread() { return true; } -bool IsNonMainThread() { return false; } - -int GetThreadName(char *str, const char *last) { return 0; } diff --git a/src/thread/thread_os2.cpp b/src/thread/thread_os2.cpp deleted file mode 100644 index d2251a9462..0000000000 --- a/src/thread/thread_os2.cpp +++ /dev/null @@ -1,155 +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 . - */ - -/** @file thread_os2.cpp OS/2 implementation of Threads. */ - -#include "../stdafx.h" -#include "thread.h" - -#define INCL_DOS -#include -#include - -#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; -} - -/** - * OS/2 version of ThreadMutex. - */ -class ThreadMutex_OS2 : public ThreadMutex { -private: - HMTX mutex; ///< The mutex. - HEV event; ///< Event for waiting. - uint recursive_count; ///< Recursive lock count. - -public: - ThreadMutex_OS2() : recursive_count(0) - { - DosCreateMutexSem(NULL, &mutex, 0, FALSE); - DosCreateEventSem(NULL, &event, 0, FALSE); - } - - ~ThreadMutex_OS2() override - { - DosCloseMutexSem(mutex); - DosCloseEventSem(event); - } - - void BeginCritical(bool allow_recursive = false) override - { - /* os2 mutex is recursive by itself */ - DosRequestMutexSem(mutex, (unsigned long) SEM_INDEFINITE_WAIT); - this->recursive_count++; - if (!allow_recursive && this->recursive_count != 1) NOT_REACHED(); - } - - void EndCritical(bool allow_recursive = false) override - { - if (!allow_recursive && this->recursive_count != 1) NOT_REACHED(); - this->recursive_count--; - DosReleaseMutexSem(mutex); - } - - void WaitForSignal() override - { - assert(this->recursive_count == 1); // Do we need to call Begin/EndCritical multiple times otherwise? - this->EndCritical(); - DosWaitEventSem(event, SEM_INDEFINITE_WAIT); - this->BeginCritical(); - } - - void SendSignal() override - { - DosPostEventSem(event); - } -}; - -/* static */ ThreadMutex *ThreadMutex::New() -{ - return new ThreadMutex_OS2(); -} - -void SetSelfAsMainThread() { } - -bool IsMainThread() { return false; } - -bool IsNonMainThread() { return false; } - -int GetThreadName(char *str, const char *last) { return 0; } diff --git a/src/thread/thread_pthread.cpp b/src/thread/thread_pthread.cpp deleted file mode 100644 index 44d3242d25..0000000000 --- a/src/thread/thread_pthread.cpp +++ /dev/null @@ -1,223 +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 . - */ - -/** @file thread_pthread.cpp POSIX pthread implementation of Threads. */ - -#include "../stdafx.h" -#include "thread.h" -#include "../string_func.h" -#include -#include - -#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; -} - -/** - * POSIX pthread version of ThreadMutex. - */ -class ThreadMutex_pthread : public ThreadMutex { -private: - pthread_mutex_t mutex; ///< The actual mutex. - pthread_cond_t condition; ///< Data for conditional waiting. - pthread_mutexattr_t attr; ///< Attributes set for the mutex. - pthread_t owner; ///< Owning thread of the mutex. - uint recursive_count; ///< Recursive lock count. - -public: - ThreadMutex_pthread() : owner(0), recursive_count(0) - { - pthread_mutexattr_init(&this->attr); - pthread_mutexattr_settype(&this->attr, PTHREAD_MUTEX_ERRORCHECK); - pthread_mutex_init(&this->mutex, &this->attr); - pthread_cond_init(&this->condition, NULL); - } - - ~ThreadMutex_pthread() override - { - int err = pthread_cond_destroy(&this->condition); - assert(err != EBUSY); - err = pthread_mutex_destroy(&this->mutex); - assert(err != EBUSY); - } - - bool IsOwnedByCurrentThread() const - { - return this->owner == pthread_self(); - } - - void BeginCritical(bool allow_recursive = false) override - { - /* pthread mutex is not recursive by itself */ - if (this->IsOwnedByCurrentThread()) { - if (!allow_recursive) NOT_REACHED(); - } else { - int err = pthread_mutex_lock(&this->mutex); - assert(err == 0); - assert(this->recursive_count == 0); - this->owner = pthread_self(); - } - this->recursive_count++; - } - - void EndCritical(bool allow_recursive = false) override - { - assert(this->IsOwnedByCurrentThread()); - if (!allow_recursive && this->recursive_count != 1) NOT_REACHED(); - this->recursive_count--; - if (this->recursive_count != 0) return; - this->owner = 0; - int err = pthread_mutex_unlock(&this->mutex); - assert(err == 0); - } - - void WaitForSignal() override - { - uint old_recursive_count = this->recursive_count; - this->recursive_count = 0; - this->owner = 0; - int err = pthread_cond_wait(&this->condition, &this->mutex); - assert(err == 0); - this->owner = pthread_self(); - this->recursive_count = old_recursive_count; - } - - void SendSignal() override - { - int err = pthread_cond_signal(&this->condition); - assert(err == 0); - } -}; - -/* static */ ThreadMutex *ThreadMutex::New() -{ - return new ThreadMutex_pthread(); -} - -static pthread_t main_thread; - -void SetSelfAsMainThread() -{ - main_thread = pthread_self(); -} - -bool IsMainThread() -{ - return main_thread == pthread_self(); -} - -bool IsNonMainThread() -{ - return main_thread != pthread_self(); -} - -int GetThreadName(char *str, const char *last) -{ -#if defined(__GLIBC__) -#if __GLIBC_PREREQ(2, 12) - char buffer[16]; - int result = pthread_getname_np(pthread_self(), buffer, sizeof(buffer)); - if (result == 0) { - return seprintf(str, last, "%s", buffer); - } -#endif -#endif - return 0; -} diff --git a/src/thread/thread_win32.cpp b/src/thread/thread_win32.cpp deleted file mode 100644 index 7f91cba6cd..0000000000 --- a/src/thread/thread_win32.cpp +++ /dev/null @@ -1,214 +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 . - */ - -/** @file thread_win32.cpp Win32 thread implementation of Threads. */ - -#include "../stdafx.h" -#include "thread.h" -#include "../debug.h" -#include "../core/alloc_func.hpp" -#include "../scope.h" -#include "../string_func.h" -#include -#include -#include -#include "../os/windows/win32.h" -#include -#include - -#include "../safeguards.h" - -static void Win32SetThreadName(uint id, const char *name); - -/** - * 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; - Win32SetThreadName(this->id, name); - 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; -} - -/** - * Win32 thread version of ThreadMutex. - */ -class ThreadMutex_Win32 : public ThreadMutex { -private: - CRITICAL_SECTION critical_section; ///< The critical section we would enter. - HANDLE event; ///< Event for signalling. - uint recursive_count; ///< Recursive lock count. - -public: - ThreadMutex_Win32() : recursive_count(0) - { - InitializeCriticalSection(&this->critical_section); - this->event = CreateEvent(NULL, FALSE, FALSE, NULL); - } - - ~ThreadMutex_Win32() override - { - DeleteCriticalSection(&this->critical_section); - CloseHandle(this->event); - } - - void BeginCritical(bool allow_recursive = false) override - { - /* windows mutex is recursive by itself */ - EnterCriticalSection(&this->critical_section); - this->recursive_count++; - if (!allow_recursive && this->recursive_count != 1) NOT_REACHED(); - } - - void EndCritical(bool allow_recursive = false) override - { - if (!allow_recursive && this->recursive_count != 1) NOT_REACHED(); - this->recursive_count--; - LeaveCriticalSection(&this->critical_section); - } - - void WaitForSignal() override - { - assert(this->recursive_count == 1); // Do we need to call Begin/EndCritical multiple times otherwise? - this->EndCritical(); - WaitForSingleObject(this->event, INFINITE); - this->BeginCritical(); - } - - void SendSignal() override - { - SetEvent(this->event); - } -}; - -/* static */ ThreadMutex *ThreadMutex::New() -{ - return new ThreadMutex_Win32(); -} - -static uint main_thread_id; - -void SetSelfAsMainThread() -{ - main_thread_id = GetCurrentThreadId(); -} - -bool IsMainThread() -{ - return main_thread_id == GetCurrentThreadId(); -} - -bool IsNonMainThread() -{ - return main_thread_id != GetCurrentThreadId(); -} - -static std::map _thread_name_map; -static ThreadMutex_Win32 _thread_name_map_mutex; - -static void Win32SetThreadName(uint id, const char *name) -{ - _thread_name_map_mutex.BeginCritical(); - _thread_name_map[id] = name; - _thread_name_map_mutex.EndCritical(); -} - -int GetThreadName(char *str, const char *last) -{ - _thread_name_map_mutex.BeginCritical(); - auto guard = scope_guard([&]() { - _thread_name_map_mutex.EndCritical(); - }); - auto iter = _thread_name_map.find(GetCurrentThreadId()); - if (iter != _thread_name_map.end()) { - return seprintf(str, last, "%s", iter->second.c_str()); - } - return 0; -} diff --git a/src/video/allegro_v.cpp b/src/video/allegro_v.cpp index 53a42393ee..c21f88d672 100644 --- a/src/video/allegro_v.cpp +++ b/src/video/allegro_v.cpp @@ -25,6 +25,7 @@ #include "../core/random_func.hpp" #include "../core/math_func.hpp" #include "../framerate_type.h" +#include "../thread.h" #include "allegro_v.h" #include diff --git a/src/video/cocoa/event.mm b/src/video/cocoa/event.mm index de812a70d6..a8108b561a 100644 --- a/src/video/cocoa/event.mm +++ b/src/video/cocoa/event.mm @@ -37,6 +37,7 @@ #include "../../core/math_func.hpp" #include "../../texteff.hpp" #include "../../window_func.h" +#include "../../thread.h" #import /* gettimeofday */ diff --git a/src/video/cocoa/fullscreen.mm b/src/video/cocoa/fullscreen.mm index 860866b336..3ce22133d2 100644 --- a/src/video/cocoa/fullscreen.mm +++ b/src/video/cocoa/fullscreen.mm @@ -30,6 +30,7 @@ #include "../../core/sort_func.hpp" #include "cocoa_v.h" #include "../../gfx_func.h" +#include "../../thread.h" #include "../../os/macosx/macos.h" /** @@ -174,7 +175,7 @@ class FullscreenSubdriver : public CocoaSubdriver { double adjustment = (target - position) / linesPerSecond; - CSleep((uint32)(adjustment * 1000)); + CSleep((uint32)adjustment * 1000); } diff --git a/src/video/dedicated_v.cpp b/src/video/dedicated_v.cpp index 58d7d04e0b..5af900080f 100644 --- a/src/video/dedicated_v.cpp +++ b/src/video/dedicated_v.cpp @@ -22,6 +22,7 @@ #include "../company_func.h" #include "../core/random_func.hpp" #include "../saveload/saveload.h" +#include "../thread.h" #include "dedicated_v.h" #ifdef __OS2__ @@ -73,6 +74,7 @@ static void DedicatedSignalHandler(int sig) # include # include # 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 +82,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 62bbb33012..5e3fb45529 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" @@ -25,6 +25,8 @@ #include "../framerate_type.h" #include "sdl_v.h" #include +#include +#include #include "../safeguards.h" @@ -36,10 +38,10 @@ 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 ThreadMutex *_draw_mutex = NULL; +static std::recursive_mutex *_draw_mutex = NULL; +/** Signal to draw the next frame. */ +static std::condition_variable_any *_draw_signal = NULL; /** Should we keep continue drawing? */ static volatile bool _draw_continue; static Palette _local_palette; @@ -169,24 +171,21 @@ static void DrawSurfaceToScreen() } } -static void DrawSurfaceToScreenThread(void *) +static void DrawSurfaceToScreenThread() { /* First tell the main thread we're started */ - _draw_mutex->BeginCritical(); - _draw_mutex->SendSignal(); + std::unique_lock lock(*_draw_mutex); + _draw_signal->notify_one(); /* Now wait for the first thing to draw! */ - _draw_mutex->WaitForSignal(); + _draw_signal->wait(*_draw_mutex); while (_draw_continue) { CheckPaletteAnim(); /* Then just draw and wait till we stop */ DrawSurfaceToScreen(); - _draw_mutex->WaitForSignal(); + _draw_signal->wait(lock); } - - _draw_mutex->EndCritical(); - _draw_thread->Exit(); } static const Dimension _default_resolutions[] = { @@ -668,26 +667,32 @@ void VideoDriver_SDL::MainLoop() CheckPaletteAnim(); + std::thread draw_thread; + std::unique_lock draw_lock; if (_draw_threaded) { /* Initialise the mutex first, because that's the thing we *need* * directly in the newly created thread. */ - _draw_mutex = ThreadMutex::New(); + _draw_mutex = new std::recursive_mutex(); if (_draw_mutex == NULL) { _draw_threaded = false; } else { - _draw_mutex->BeginCritical(); + draw_lock = std::unique_lock(*_draw_mutex); + _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) { - _draw_mutex->EndCritical(); + draw_lock.unlock(); + draw_lock.release(); delete _draw_mutex; + delete _draw_signal; _draw_mutex = NULL; + _draw_signal = NULL; } else { /* Wait till the draw mutex has started itself. */ - _draw_mutex->WaitForSignal(); + _draw_signal->wait(*_draw_mutex); } } } @@ -752,19 +757,19 @@ void VideoDriver_SDL::MainLoop() /* The gameloop is the part that can run asynchronously. The rest * except sleeping can't. */ - if (_draw_mutex != NULL) _draw_mutex->EndCritical(); + if (_draw_mutex != NULL) draw_lock.unlock(); GameLoop(); - if (_draw_mutex != NULL) _draw_mutex->BeginCritical(); + if (_draw_mutex != NULL) draw_lock.lock(); UpdateWindows(); _local_palette = _cur_palette; } else { /* Release the thread while sleeping */ - if (_draw_mutex != NULL) _draw_mutex->EndCritical(); + if (_draw_mutex != NULL) draw_lock.unlock(); CSleep(1); - if (_draw_mutex != NULL) _draw_mutex->BeginCritical(); + if (_draw_mutex != NULL) draw_lock.lock(); NetworkDrawChatMessage(); DrawMouseCursor(); @@ -772,7 +777,7 @@ void VideoDriver_SDL::MainLoop() /* End of the critical part. */ if (_draw_mutex != NULL && !HasModalProgress()) { - _draw_mutex->SendSignal(); + _draw_signal->notify_one(); } else { /* Oh, we didn't have threads, then just draw unthreaded */ CheckPaletteAnim(); @@ -784,29 +789,32 @@ void VideoDriver_SDL::MainLoop() _draw_continue = false; /* Sending signal if there is no thread blocked * is very valid and results in noop */ - _draw_mutex->SendSignal(); - _draw_mutex->EndCritical(); - _draw_thread->Join(); + _draw_signal->notify_one(); + if (draw_lock.owns_lock()) draw_lock.unlock(); + draw_lock.release(); + draw_thread.join(); delete _draw_mutex; - delete _draw_thread; + delete _draw_signal; _draw_mutex = NULL; - _draw_thread = NULL; + _draw_signal = NULL; } } bool VideoDriver_SDL::ChangeResolution(int w, int h) { - if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true); - bool ret = CreateMainSurface(w, h); - if (_draw_mutex != NULL) _draw_mutex->EndCritical(true); - return ret; + std::unique_lock lock; + if (_draw_mutex != NULL) lock = std::unique_lock(*_draw_mutex); + + return CreateMainSurface(w, h); } bool VideoDriver_SDL::ToggleFullscreen(bool fullscreen) { - if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true); + std::unique_lock lock; + if (_draw_mutex != NULL) lock = std::unique_lock(*_draw_mutex); + _fullscreen = fullscreen; GetVideoModes(); // get the list of available video modes bool ret = _num_resolutions != 0 && CreateMainSurface(_cur_resolution.width, _cur_resolution.height); @@ -816,7 +824,6 @@ bool VideoDriver_SDL::ToggleFullscreen(bool fullscreen) _fullscreen ^= true; } - if (_draw_mutex != NULL) _draw_mutex->EndCritical(true); return ret; } @@ -827,12 +834,12 @@ bool VideoDriver_SDL::AfterBlitterChange() void VideoDriver_SDL::AcquireBlitterLock() { - if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true); + if (_draw_mutex != NULL) _draw_mutex->lock(); } void VideoDriver_SDL::ReleaseBlitterLock() { - if (_draw_mutex != NULL) _draw_mutex->EndCritical(true); + if (_draw_mutex != NULL) _draw_mutex->unlock(); } #endif /* WITH_SDL */ diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 6cee4fef28..382c0d8fc6 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" @@ -27,6 +27,8 @@ #include "win32_v.h" #include #include +#include +#include #include "../safeguards.h" @@ -65,12 +67,10 @@ 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 ThreadMutex *_draw_mutex = NULL; -/** Event that is signaled when the drawing thread has finished initializing. */ -static HANDLE _draw_thread_initialized = NULL; +static std::recursive_mutex *_draw_mutex = NULL; +/** Signal to draw the next frame. */ +static std::condition_variable_any *_draw_signal = NULL; /** Should we keep continue drawing? */ static volatile bool _draw_continue; /** Local copy of the palette for use in the drawing thread. */ @@ -393,14 +393,14 @@ static void PaintWindow(HDC dc) DeleteDC(dc2); } -static void PaintWindowThread(void *) +static void PaintWindowThread() { /* First tell the main thread we're started */ - _draw_mutex->BeginCritical(); - SetEvent(_draw_thread_initialized); + std::unique_lock lock(*_draw_mutex); + _draw_signal->notify_one(); /* Now wait for the first thing to draw! */ - _draw_mutex->WaitForSignal(); + _draw_signal->wait(*_draw_mutex); while (_draw_continue) { /* Convert update region from logical to device coordinates. */ @@ -422,11 +422,8 @@ static void PaintWindowThread(void *) /* Flush GDI buffer to ensure drawing here doesn't conflict with any GDI usage in the main WndProc. */ GdiFlush(); - _draw_mutex->WaitForSignal(); + _draw_signal->wait(*_draw_mutex); } - - _draw_mutex->EndCritical(); - _draw_thread->Exit(); } /** Forward key presses to the window system. */ @@ -658,7 +655,7 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP /* Mark the window as updated, otherwise Windows would send more WM_PAINT messages. */ ValidateRect(hwnd, NULL); - _draw_mutex->SendSignal(); + _draw_signal->notify_one(); } else { PAINTSTRUCT ps; @@ -1152,7 +1149,7 @@ const char *VideoDriver_Win32::Start(const char * const *parm) MarkWholeScreenDirty(); - _draw_threaded = GetDriverParam(parm, "no_threads") == NULL && GetDriverParam(parm, "no_thread") == NULL && GetCPUCoreCount() > 1; + _draw_threaded = GetDriverParam(parm, "no_threads") == NULL && GetDriverParam(parm, "no_thread") == NULL && std::thread::hardware_concurrency() > 1; return NULL; } @@ -1189,28 +1186,37 @@ 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 draw_lock; + if (_draw_threaded) { /* Initialise the mutex first, because that's the thing we *need* * directly in the newly created thread. */ - _draw_mutex = ThreadMutex::New(); - _draw_thread_initialized = CreateEvent(NULL, FALSE, FALSE, NULL); - if (_draw_mutex == NULL || _draw_thread_initialized == NULL) { + try { + _draw_signal = new std::condition_variable_any(); + _draw_mutex = new std::recursive_mutex(); + } catch (...) { _draw_threaded = false; - } else { + } + + if (_draw_threaded) { + draw_lock = std::unique_lock(*_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) { + draw_lock.unlock(); + draw_lock.release(); delete _draw_mutex; + delete _draw_signal; _draw_mutex = NULL; - CloseHandle(_draw_thread_initialized); - _draw_thread_initialized = NULL; + _draw_signal = NULL; } else { DEBUG(driver, 1, "Threaded drawing enabled"); /* Wait till the draw thread has started itself. */ - WaitForSingleObject(_draw_thread_initialized, INFINITE); - _draw_mutex->BeginCritical(); + _draw_signal->wait(*_draw_mutex); } } } @@ -1227,7 +1233,7 @@ void VideoDriver_Win32::MainLoop() if (EditBoxInGlobalFocus()) TranslateMessage(&mesg); DispatchMessage(&mesg); } - if (_exit_game) return; + if (_exit_game) break; #if defined(_DEBUG) if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0 && @@ -1270,9 +1276,9 @@ void VideoDriver_Win32::MainLoop() /* The game loop is the part that can run asynchronously. * The rest except sleeping can't. */ - if (_draw_threaded) _draw_mutex->EndCritical(); + if (_draw_threaded) draw_lock.unlock(); GameLoop(); - if (_draw_threaded) _draw_mutex->BeginCritical(); + if (_draw_threaded) draw_lock.lock(); if (_force_full_redraw) MarkWholeScreenDirty(); @@ -1283,9 +1289,9 @@ void VideoDriver_Win32::MainLoop() GdiFlush(); /* Release the thread while sleeping */ - if (_draw_threaded) _draw_mutex->EndCritical(); + if (_draw_threaded) draw_lock.unlock(); Sleep(1); - if (_draw_threaded) _draw_mutex->BeginCritical(); + if (_draw_threaded) draw_lock.lock(); NetworkDrawChatMessage(); DrawMouseCursor(); @@ -1296,35 +1302,37 @@ void VideoDriver_Win32::MainLoop() _draw_continue = false; /* Sending signal if there is no thread blocked * is very valid and results in noop */ - _draw_mutex->SendSignal(); - _draw_mutex->EndCritical(); - _draw_thread->Join(); + _draw_signal->notify_all(); + if (draw_lock.owns_lock()) draw_lock.unlock(); + draw_lock.release(); + draw_thread.join(); - CloseHandle(_draw_thread_initialized); delete _draw_mutex; - delete _draw_thread; + delete _draw_signal; + + _draw_mutex = NULL; } } bool VideoDriver_Win32::ChangeResolution(int w, int h) { - if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true); + std::unique_lock lock; + if (_draw_mutex != NULL) lock = std::unique_lock(*_draw_mutex); + if (_window_maximize) ShowWindow(_wnd.main_wnd, SW_SHOWNORMAL); _wnd.width = _wnd.width_org = w; _wnd.height = _wnd.height_org = h; - bool ret = this->MakeWindow(_fullscreen); // _wnd.fullscreen screws up ingame resolution switching - if (_draw_mutex != NULL) _draw_mutex->EndCritical(true); - return ret; + return this->MakeWindow(_fullscreen); // _wnd.fullscreen screws up ingame resolution switching } bool VideoDriver_Win32::ToggleFullscreen(bool full_screen) { - if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true); - bool ret = this->MakeWindow(full_screen); - if (_draw_mutex != NULL) _draw_mutex->EndCritical(true); - return ret; + std::unique_lock lock; + if (_draw_mutex != NULL) lock = std::unique_lock(*_draw_mutex); + + return this->MakeWindow(full_screen); } bool VideoDriver_Win32::AfterBlitterChange() @@ -1334,19 +1342,20 @@ bool VideoDriver_Win32::AfterBlitterChange() void VideoDriver_Win32::AcquireBlitterLock() { - if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true); + if (_draw_mutex != NULL) _draw_mutex->lock(); } void VideoDriver_Win32::ReleaseBlitterLock() { - if (_draw_mutex != NULL) _draw_mutex->EndCritical(true); + if (_draw_mutex != NULL) _draw_mutex->unlock(); } void VideoDriver_Win32::EditBoxLostFocus() { - if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true); + std::unique_lock lock; + if (_draw_mutex != NULL) lock = std::unique_lock(*_draw_mutex); + CancelIMEComposition(_wnd.main_wnd); SetCompositionPos(_wnd.main_wnd); SetCandidatePos(_wnd.main_wnd); - if (_draw_mutex != NULL) _draw_mutex->EndCritical(true); }