From a1f9818037aec1c99c2bd5c6bea5a38ca1717ceb Mon Sep 17 00:00:00 2001 From: hondacrx Date: Sun, 13 May 2018 20:27:55 -0400 Subject: [PATCH] Core/Maps: Enabled maps updater. and reduce cpu usage (i hope) --- .../Threading/ProducerConsumerQueue.cs | 91 +++++++++++++++ Source/Game/Maps/MapManager.cs | 13 ++- Source/Game/Maps/MapUpdater.cs | 109 +++++++++++------- 3 files changed, 167 insertions(+), 46 deletions(-) create mode 100644 Source/Framework/Threading/ProducerConsumerQueue.cs diff --git a/Source/Framework/Threading/ProducerConsumerQueue.cs b/Source/Framework/Threading/ProducerConsumerQueue.cs new file mode 100644 index 000000000..3f8091d4d --- /dev/null +++ b/Source/Framework/Threading/ProducerConsumerQueue.cs @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012-2018 CypherCore + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +using System.Collections.Generic; +using System.Threading; + +namespace Framework.Threading +{ + public class ProducerConsumerQueue + { + object _queueLock = new object(); + Queue _queue = new Queue(); + volatile bool _shutdown; + + public ProducerConsumerQueue() + { + _shutdown = false; + } + + public void Push(T value) + { + lock (_queueLock) + { + _queue.Enqueue(value); + Monitor.PulseAll(_queueLock); + } + } + + public bool Empty() + { + lock (_queueLock) + return _queue.Count == 0; + } + + public bool Pop(out T value) + { + value = default(T); + lock (_queueLock) + { + if (_queue.Count == 0 || _shutdown) + return false; + + value = _queue.Dequeue(); + return true; + } + } + + public void WaitAndPop(out T value) + { + value = default(T); + lock (_queueLock) + { + while (_queue.Count == 0 && !_shutdown) + Monitor.Wait(_queueLock); + + if (_queue.Count == 0 || _shutdown) + return; + + value = _queue.Dequeue(); + } + } + + public void Cancel() + { + lock (_queueLock) + { + while (_queue.Count != 0) + { + T value = _queue.Dequeue(); + } + + _shutdown = true; + Monitor.PulseAll(_queueLock); + } + } + } +} diff --git a/Source/Game/Maps/MapManager.cs b/Source/Game/Maps/MapManager.cs index c964e7198..03618afcc 100644 --- a/Source/Game/Maps/MapManager.cs +++ b/Source/Game/Maps/MapManager.cs @@ -224,10 +224,10 @@ namespace Game.Entities return; var time = (uint)i_timer.GetCurrent(); - foreach (var map in i_maps.Values.ToList()) + foreach (var map in i_maps.Values) { if (m_updater != null) - m_updater.Enqueue(map, (uint)i_timer.GetCurrent()); + m_updater.ScheduleUpdate(map, (uint)i_timer.GetCurrent()); else map.Update(time); } @@ -235,7 +235,7 @@ namespace Game.Entities if (m_updater != null) m_updater.Wait(); - foreach (var map in i_maps.ToList()) + foreach (var map in i_maps) map.Value.DelayedUpdate(time); i_timer.SetCurrent(0); @@ -269,11 +269,14 @@ namespace Game.Entities foreach (var pair in i_maps) pair.Value.UnlinkAllChildTerrainMaps(); - foreach (var pair in i_maps.ToList()) + foreach (var pair in i_maps) { pair.Value.UnloadAll(); i_maps.Remove(pair.Key); } + + if (m_updater != null) + m_updater.Deactivate(); } public uint GetNumInstances() @@ -281,7 +284,7 @@ namespace Game.Entities lock (_mapsLock) { uint ret = 0; - foreach (var pair in i_maps.ToList()) + foreach (var pair in i_maps) { Map map = pair.Value; if (!map.Instanceable()) diff --git a/Source/Game/Maps/MapUpdater.cs b/Source/Game/Maps/MapUpdater.cs index 59ac9b021..51087fdbd 100644 --- a/Source/Game/Maps/MapUpdater.cs +++ b/Source/Game/Maps/MapUpdater.cs @@ -15,76 +15,96 @@ * along with this program. If not, see . */ -using System; -using System.Collections.Generic; +using Framework.Threading; using System.Threading; namespace Game.Maps { - public class MapUpdater : IDisposable + public class MapUpdater { + ProducerConsumerQueue _queue = new ProducerConsumerQueue(); + + Thread[] _workerThreads; + volatile bool _cancelationToken; + + object _lock = new object(); + int _pendingRequests; + public MapUpdater(int numThreads) { - _queue = new Queue(); - - autoResetEvent = new AutoResetEvent[numThreads]; + _workerThreads = new Thread[numThreads]; for (var i = 0; i < numThreads; ++i) { - autoResetEvent[i] = new AutoResetEvent(false); - ThreadPool.QueueUserWorkItem(new WaitCallback(OnEnqueue), autoResetEvent[i]); + _workerThreads[i] = new Thread(WorkerThread); + _workerThreads[i].Start(); } + } - public void Enqueue(Map map, uint diff) + public void Deactivate() { - lock (_syncLock) - { - _queue.Enqueue(new MapUpdateRequest(map, diff)); - Monitor.PulseAll(_syncLock); - } - } + _cancelationToken = true; - protected void OnEnqueue(object state) - { - while (true) - { - lock (_syncLock) - { - if (_queue.Count == 0) - { - ((AutoResetEvent)state).Set(); - Monitor.Wait(_syncLock); - } + Wait(); - if (_queue.Count > 0) - { - _queue.Dequeue().Call(); - } - } + _queue.Cancel(); + + foreach (var thread in _workerThreads) + { + thread.Join(); } } public void Wait() { - WaitHandle.WaitAll(autoResetEvent); - } - - public void Dispose() - { - lock (_syncLock) + lock (_lock) { - Monitor.PulseAll(_syncLock); + while (_pendingRequests > 0) + Monitor.Wait(_lock); } } - private Queue _queue; - private object _syncLock = new object(); - private WaitHandle[] autoResetEvent; + public void ScheduleUpdate(Map map, uint diff) + { + lock (_lock) + { + ++_pendingRequests; + + _queue.Push(new MapUpdateRequest(map, this, diff)); + } + } + + public void UpdateFinished() + { + lock (_lock) + { + --_pendingRequests; + + Monitor.PulseAll(_lock); + } + } + + void WorkerThread() + { + while (true) + { + MapUpdateRequest request; + + _queue.WaitAndPop(out request); + + if (_cancelationToken) + return; + + request.Call(); + UpdateFinished(); + } + } } public class MapUpdateRequest { Map m_map; + MapUpdater m_updater; uint m_diff; public MapUpdateRequest(Map m, uint d) @@ -93,6 +113,13 @@ namespace Game.Maps m_diff = d; } + public MapUpdateRequest(Map m, MapUpdater u, uint d) + { + m_map = m; + m_updater = u; + m_diff = d; + } + public void Call() { m_map.Update(m_diff);