diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 04325b1dc5..795b3ddbf4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -539,6 +539,8 @@ add_files( window_func.h window_gui.h window_type.h + worker_thread.cpp + worker_thread.h zoom_func.h zoom_type.h zoning.h diff --git a/src/openttd.cpp b/src/openttd.cpp index db18bbaba2..4c6aff8354 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -84,6 +84,7 @@ #include "debug_desync.h" #include "event_logs.h" #include "tunnelbridge.h" +#include "worker_thread.h" #include "linkgraph/linkgraphschedule.h" #include "tracerestrict.h" @@ -991,8 +992,12 @@ int openttd_main(int argc, char *argv[]) /* ScanNewGRFFiles now has control over the scanner. */ RequestNewGRFScan(scanner.release()); + _general_worker_pool.Start("ottd:worker", 8); + VideoDriver::GetInstance()->MainLoop(); + _general_worker_pool.Stop(); + WaitTillSaved(); /* only save config if we have to */ diff --git a/src/worker_thread.cpp b/src/worker_thread.cpp new file mode 100644 index 0000000000..64ef0325d1 --- /dev/null +++ b/src/worker_thread.cpp @@ -0,0 +1,82 @@ +/* + * 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 worker_thread.cpp Worker thread pool utility. */ + +#include "stdafx.h" +#include "worker_thread.h" +#include "thread.h" + +#include "safeguards.h" + +WorkerThreadPool _general_worker_pool; + +void WorkerThreadPool::Start(const char *thread_name, uint max_workers) +{ + uint cpus = std::thread::hardware_concurrency(); + if (cpus <= 1) return; + + std::lock_guard lk(this->lock); + + this->exit = false; + + uint worker_target = std::min(max_workers, cpus); + if (this->workers >= worker_target) return; + + uint new_workers = worker_target - this->workers; + + for (uint i = 0; i < new_workers; i++) { + this->workers++; + if (!StartNewThread(nullptr, thread_name, &WorkerThreadPool::Run, this)) { + this->workers--; + return; + } + } +} + +void WorkerThreadPool::Stop() +{ + std::unique_lock lk(this->lock); + this->exit = true; + this->empty_cv.notify_all(); + this->done_cv.wait(lk, [this]() { return this->workers == 0; }); +} + +void WorkerThreadPool::EnqueueJob(WorkerJobFunc *func, void *data1, void *data2, void *data3) +{ + std::unique_lock lk(this->lock); + if (this->workers == 0) { + /* Just execute it here and now */ + lk.unlock(); + func(data1, data2, data3); + return; + } + bool notify = this->jobs.empty(); + this->jobs.push({ func, data1, data2, data3 }); + lk.unlock(); + if (notify) this->empty_cv.notify_one(); +} + +void WorkerThreadPool::Run(WorkerThreadPool *pool) +{ + std::unique_lock lk(pool->lock); + while (!pool->exit || !pool->jobs.empty()) { + if (pool->jobs.empty()) { + pool->empty_cv.wait(lk); + } else { + WorkerJob job = pool->jobs.front(); + pool->jobs.pop(); + lk.unlock(); + job.func(job.data1, job.data2, job.data3); + lk.lock(); + } + } + pool->workers--; + if (pool->workers == 0) { + pool->done_cv.notify_all(); + } +} diff --git a/src/worker_thread.h b/src/worker_thread.h new file mode 100644 index 0000000000..2a2b2134a6 --- /dev/null +++ b/src/worker_thread.h @@ -0,0 +1,55 @@ +/* + * 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 worker_thread.h Worker thread pool utility. */ + +#ifndef WORKER_THREAD_H +#define WORKER_THREAD_H + +#include +#include +#include +#if defined(__MINGW32__) +#include "3rdparty/mingw-std-threads/mingw.mutex.h" +#include "3rdparty/mingw-std-threads/mingw.condition_variable.h" +#endif + +typedef void WorkerJobFunc(void *, void *, void *); + +struct WorkerThreadPool { +private: + struct WorkerJob { + WorkerJobFunc *func; + void *data1; + void *data2; + void *data3; + }; + + uint workers = 0; + bool exit = false; + std::mutex lock; + std::queue jobs; + std::condition_variable empty_cv; + std::condition_variable done_cv; + + static void Run(WorkerThreadPool *pool); + +public: + + void Start(const char *thread_name, uint max_workers); + void Stop(); + void EnqueueJob(WorkerJobFunc *func, void *data1 = nullptr, void *data2 = nullptr, void *data3 = nullptr); + + ~WorkerThreadPool() + { + this->Stop(); + } +}; + +extern WorkerThreadPool _general_worker_pool; + +#endif /* WORKER_THREAD_H */