diff --git a/src/common/Threading/PCQueue.h b/src/common/Threading/PCQueue.h index 57f3b08ef..1efb494ba 100644 --- a/src/common/Threading/PCQueue.h +++ b/src/common/Threading/PCQueue.h @@ -28,10 +28,11 @@ private: std::mutex _queueLock; std::queue _queue; std::condition_variable _condition; + std::atomic _cancel; std::atomic _shutdown; public: - ProducerConsumerQueue() : _shutdown(false) { } + ProducerConsumerQueue() : _cancel(false), _shutdown(false) { } void Push(const T& value) { @@ -57,10 +58,8 @@ public: { std::lock_guard lock(_queueLock); - if (_queue.empty() || _shutdown) - { + if (_queue.empty() || _cancel) return false; - } value = _queue.front(); @@ -75,21 +74,18 @@ public: // we could be using .wait(lock, predicate) overload here but it is broken // https://connect.microsoft.com/VisualStudio/feedback/details/1098841 - while (_queue.empty() && !_shutdown) - { + while (_queue.empty() && !_cancel && !_shutdown) _condition.wait(lock); - } - if (_queue.empty() || _shutdown) - { + if (_queue.empty() || _cancel) return; - } value = _queue.front(); _queue.pop(); } + // Clears the queue and will immediately stop any consumers void Cancel() { std::unique_lock lock(_queueLock); @@ -103,11 +99,18 @@ public: _queue.pop(); } - _shutdown = true; + _cancel = true; _condition.notify_all(); } + // Graceful stop, will wait for queue to become empty before stopping consumers + void Shutdown() + { + _shutdown = true; + _condition.notify_all(); + } + private: template typename std::enable_if::value>::type DeleteQueuedObject(E& obj) { delete obj; } diff --git a/src/server/database/Database/DatabaseWorker.cpp b/src/server/database/Database/DatabaseWorker.cpp index 527eb9b50..49965e183 100644 --- a/src/server/database/Database/DatabaseWorker.cpp +++ b/src/server/database/Database/DatabaseWorker.cpp @@ -23,16 +23,11 @@ DatabaseWorker::DatabaseWorker(ProducerConsumerQueue* newQueue, M { _connection = connection; _queue = newQueue; - _cancelationToken = false; _workerThread = std::thread(&DatabaseWorker::WorkerThread, this); } DatabaseWorker::~DatabaseWorker() { - _cancelationToken = true; - - _queue->Cancel(); - _workerThread.join(); } @@ -47,7 +42,7 @@ void DatabaseWorker::WorkerThread() _queue->WaitAndPop(operation); - if (_cancelationToken || !operation) + if (!operation) return; operation->SetConnection(_connection); diff --git a/src/server/database/Database/DatabaseWorker.h b/src/server/database/Database/DatabaseWorker.h index bc6724216..7ce560b3d 100644 --- a/src/server/database/Database/DatabaseWorker.h +++ b/src/server/database/Database/DatabaseWorker.h @@ -41,8 +41,6 @@ private: void WorkerThread(); std::thread _workerThread; - std::atomic _cancelationToken; - DatabaseWorker(DatabaseWorker const& right) = delete; DatabaseWorker& operator=(DatabaseWorker const& right) = delete; }; diff --git a/src/server/database/Database/DatabaseWorkerPool.cpp b/src/server/database/Database/DatabaseWorkerPool.cpp index 0fd20d4b4..968e4e428 100644 --- a/src/server/database/Database/DatabaseWorkerPool.cpp +++ b/src/server/database/Database/DatabaseWorkerPool.cpp @@ -112,7 +112,11 @@ uint32 DatabaseWorkerPool::Open() template void DatabaseWorkerPool::Close() { - LOG_INFO("sql.driver", "Closing down DatabasePool '{}'.", GetDatabaseName()); + LOG_INFO("sql.driver", "Closing down DatabasePool '{}'. Waiting for {} queries to finish...", GetDatabaseName(), _queue->Size()); + + // Gracefully close async query queue, worker threads will block when the destructor + // is called from the .clear() functions below until the queue is empty + _queue->Shutdown(); //! Closes the actualy MySQL connection. _connections[IDX_ASYNC].clear(); @@ -432,6 +436,7 @@ uint32 DatabaseWorkerPool::OpenConnections(InternalIndex type, uint8 numConne if (uint32 error = connection->Open()) { // Failed to open a connection or invalid version, abort and cleanup + _queue->Cancel(); _connections[type].clear(); return error; } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 72ea18e99..01aebe618 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -95,11 +95,6 @@ #include #include -namespace -{ - TaskScheduler playersSaveScheduler; -} - std::atomic_long World::_stopEvent = false; uint8 World::_exitCode = SHUTDOWN_EXIT_CODE; uint32 World::m_worldLoopCounter = 0; @@ -2497,11 +2492,6 @@ void World::Update(uint32 diff) sScriptMgr->OnWorldUpdate(diff); } - { - METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update playersSaveScheduler")); - playersSaveScheduler.Update(diff); - } - { METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update metrics")); // Stats logger update @@ -2691,31 +2681,6 @@ void World::ShutdownServ(uint32 time, uint32 options, uint8 exitcode, const std: _shutdownMask = options; _exitCode = exitcode; - auto const& playersOnline = GetActiveSessionCount(); - - if (time < 5 && playersOnline) - { - // Set time to 5s for save all players - time = 5; - } - - playersSaveScheduler.CancelAll(); - - if (time >= 5) - { - playersSaveScheduler.Schedule(Seconds(time - 5), [this](TaskContext /*context*/) - { - if (!GetActiveSessionCount()) - { - LOG_INFO("server", "> No players online. Skip save before shutdown"); - return; - } - - LOG_INFO("server", "> Save players before shutdown server"); - ObjectAccessor::SaveAllPlayers(); - }); - } - LOG_WARN("server", "Time left until shutdown/restart: {}", time); ///- If the shutdown time is 0, set m_stopEvent (except if shutdown is 'idle' with remaining sessions)