diff --git a/src/Map/Map.cs b/src/Map/Map.cs index 42b6e15..4b27666 100644 --- a/src/Map/Map.cs +++ b/src/Map/Map.cs @@ -10,6 +10,7 @@ namespace Mapbox.Map { using System.Linq; using System.Threading; using System.IO; + using System.ComponentModel; /// /// The Mapbox Map abstraction will take care of fetching and decoding @@ -58,7 +59,7 @@ private void OnQueueEmpty() { #endregion - private int _MainThreadId; + private readonly SynchronizationContext syncContext; private bool _IsDisposed = false; private bool _PauseTileUpdates = false; private TileFetcher _TileFetcher; @@ -79,14 +80,13 @@ private void OnQueueEmpty() { /// Maximum number of tiles to cache in memory. /// Size of threadpool for paralell tile fetching. public Map( - int mainThreadId - , IFileSource fileSource + IFileSource fileSource , uint memoryTileCacheMin = 9 , uint memoryTileCacheMax = 256 , uint numberOfThreads = 4 ) { - _MainThreadId = mainThreadId; + syncContext = AsyncOperationManager.SynchronizationContext; if(null == fileSource) { throw new ArgumentNullException("fileSource"); } @@ -100,8 +100,7 @@ int mainThreadId _Zoom = 0; _TileFetcher = new TileFetcher( - _MainThreadId - , fileSource + fileSource , (int)memoryTileCacheMin , (int)memoryTileCacheMax , null @@ -314,20 +313,7 @@ public void DownloadTiles() { private void TileFetcher_QueueEmpty(object sender, EventArgs e) { - //if (UnityToolbag.Dispatcher.isMainThread) - //{ - // OnQueueEmpty(); - //} - //else - //{ - // UnityToolbag.Dispatcher.Invoke(() => - // { - // OnQueueEmpty(); - // }); - //} - Mapbox.Threading.Dispatcher.Invoke(_MainThreadId, () => { - OnQueueEmpty(); - }); + syncContext.Post(delegate { OnQueueEmpty(); }, null); } @@ -336,34 +322,28 @@ private void TileFetcher_TileReceived(object sender, TileFetcherTileReceivedEven } private void addTile(byte[] tileData, CanonicalTileId tileId, bool hasError, string errorMessage) { + + T tile = new T(); + tile.Id = tileId; + //clone byte array to get rid of references //TODO: profile if this really helps - byte[] localTileData = null; - using(MemoryStream ms = new MemoryStream(tileData)) { - localTileData = ms.ToArray(); + if(null != tileData) { + byte[] localTileData = null; + using(MemoryStream ms = new MemoryStream(tileData)) { + localTileData = ms.ToArray(); + } + tile.ParseTileData(localTileData); } - T tile = new T(); - tile.Id = tileId; - tile.ParseTileData(localTileData); + tile.SetState(Tile.State.Loaded); - if(hasError) { tile.SetError(errorMessage); } + if(hasError) { + tile.SetError(errorMessage); + } lock(_TilesLock) { _Tiles.Add(tile); } - //if (UnityToolbag.Dispatcher.isMainThread) - //{ - // OnTileReceived(tile); - //} - //else - //{ - // UnityToolbag.Dispatcher.Invoke(() => - // { - // OnTileReceived(tile); - // }); - //} - Mapbox.Threading.Dispatcher.Invoke(_MainThreadId, () => { - OnTileReceived(tile); - }); + syncContext.Post(delegate { OnTileReceived(tile); }, null); } diff --git a/src/Map/TileFetcher.cs b/src/Map/TileFetcher.cs index 3ed33f3..e35a2b6 100644 --- a/src/Map/TileFetcher.cs +++ b/src/Map/TileFetcher.cs @@ -23,7 +23,6 @@ public byte[] Get(CanonicalTileId index) { } } - private int _MainThreadId; private IFileSource _FileSource; private MemoryCache _volatileCache; private ITileCache _permaCache; @@ -41,8 +40,8 @@ public byte[] Get(CanonicalTileId index) { /// min. number of tiles in memory cache /// max. number of tiles in memory cache /// The perma cache - internal TileFetcher(int mainThreadId, IFileSource fileSource, Tile tile, int minTiles, int maxTiles, ITileCache permaCache) - : this(mainThreadId, fileSource, minTiles, maxTiles, permaCache, 4) { + internal TileFetcher(IFileSource fileSource, Tile tile, int minTiles, int maxTiles, ITileCache permaCache) + : this(fileSource, minTiles, maxTiles, permaCache, 4) { } /// @@ -54,13 +53,12 @@ internal TileFetcher(int mainThreadId, IFileSource fileSource, Tile tile, int mi /// The perma cache /// The maximum number of threads used to get the tiles internal TileFetcher( - int mainThreadId - , IFileSource fileSource + IFileSource fileSource , int minTiles , int maxTiles , ITileCache permaCache, - int maxNumberOfThreads) { - _MainThreadId = mainThreadId; + int maxNumberOfThreads + ) { _FileSource = fileSource; _volatileCache = new MemoryCache(minTiles, maxTiles); _permaCache = permaCache ?? NoopCache.Instance; @@ -149,57 +147,42 @@ private object GetTileOnThread(object parameter) { try { _openTileRequests.TryAdd(tileId, 1); - Mapbox.Threading.Dispatcher.Invoke(_MainThreadId, () => { - try { - _FileSource.Request(tileUrl, (Response response) => { - if(!string.IsNullOrEmpty(response.Error)) { - //TODO: evaluate headers sent by server, or do this in IFileSource - //if (null != response.Headers) - //{ - // string hdrs = ""; - // foreach (var hdr in response.Headers) - // { - // hdrs += string.Format("{0}: {1}\n", hdr.Key, hdr.Value); - // } - // UnityEngine.Debug.LogErrorFormat("+++++ TileFetcher.GetTileOnThread(), _FileSource response.Error: \n[{0}]\n[{1}]\nheaders:\n{2}", tileUrl, response.Error, hdrs); - //} - } - result = response.Data; - if(null == result) { - errorMessage = "+++++ TileFetcher.GetTileOnThread(), no data receiced, " + response.Error; - } else { - try { - result = Compression.Decompress(result); - } - catch(Exception exDecompress) { - string msg = string.Format("+++++ TileFetcher.GetTileOnThread(), exception: [{0}], {1}", exDecompress, response.Error); - errorMessage = msg; -#if UNITY_EDITOR - UnityEngine.Debug.LogError(msg); -#else - System.Diagnostics.Debug.WriteLine(msg, "ERROR"); -#endif - } - } - fetched = true; - }); + _FileSource.Request(tileUrl, (Response response) => { + if(!string.IsNullOrEmpty(response.Error)) { + //TODO: evaluate headers sent by server, or do this in IFileSource + //if (null != response.Headers) + //{ + // string hdrs = ""; + // foreach (var hdr in response.Headers) + // { + // hdrs += string.Format("{0}: {1}\n", hdr.Key, hdr.Value); + // } + // UnityEngine.Debug.LogErrorFormat("+++++ TileFetcher.GetTileOnThread(), _FileSource response.Error: \n[{0}]\n[{1}]\nheaders:\n{2}", tileUrl, response.Error, hdrs); + //} } - catch(Exception e) { - PreserveStackTrace(e); - string msg = string.Format("+++++ TileFetcher.GetTileOnThread(), exception: [{0}]", e); - errorMessage = msg; + result = response.Data; + if(null == result) { + errorMessage = "+++++ TileFetcher.GetTileOnThread(), no data receiced, " + response.Error; + } else { + try { + result = Compression.Decompress(result); + } + catch(Exception exDecompress) { + string msg = string.Format("+++++ TileFetcher.GetTileOnThread(), exception: [{0}], {1}", exDecompress, response.Error); + errorMessage = msg; #if UNITY_EDITOR - UnityEngine.Debug.LogError(msg); + UnityEngine.Debug.LogError(msg); #else - System.Diagnostics.Debug.WriteLine(msg, "ERROR"); + System.Diagnostics.Debug.WriteLine(msg, "ERROR"); #endif - fetched = true; + } } + fetched = true; }); } - catch(Exception ex) { + PreserveStackTrace(ex); string msg = string.Format("+++++ TileFetcher.GetTileOnThread(), exception: [{0}]", ex); errorMessage = msg; #if UNITY_EDITOR @@ -210,8 +193,7 @@ private object GetTileOnThread(object parameter) { fetched = true; } - //HACK: couldn't find a way to make UnityToolbag.Dispatcher.Invoke() work - //only InvokeAsync did the job + //HACK: wait till request has finish while(!fetched) { Thread.Sleep(5); } diff --git a/src/Utils/Threading.cs b/src/Utils/Threading.cs deleted file mode 100644 index 5f9b757..0000000 --- a/src/Utils/Threading.cs +++ /dev/null @@ -1,74 +0,0 @@ -//based on https://github.com/nickgravelyn/UnityToolbag/tree/master/Dispatcher - -using System; -using System.Collections.Generic; -using System.Threading; -using UnityEngine; - -namespace Mapbox.Threading { - - - public class Dispatcher { - - - public static bool IsMainThread(int mainThreadId) { - //Debug.LogFormat("IsMainThread, current threadID:{0} mainThreadId:{1}", Thread.CurrentThread.ManagedThreadId, mainThreadId); - return Thread.CurrentThread.ManagedThreadId == mainThreadId; - } - - - private static Dispatcher _Instance; - private static object _LockActions = new object(); - private static readonly Queue _Actions = new Queue(); - - - public static void InvokeAsync(int mainThreadId, Action action) { - - if(IsMainThread(mainThreadId)) { - // Don't bother queuing work on the main thread; just execute it. - action(); - } else { - //var myDelegate = new Action(delegate (Action action2) - //{ - // action2(); - //}); - //myDelegate.Invoke(action); - lock(_LockActions) { - _Actions.Enqueue(action); - } - } - } - - - /// - /// Queues an action to be invoked on the main game thread and blocks the - /// current thread until the action has been executed. - /// - /// The action to be queued. - public static void Invoke(int mainThreadId, Action action) { - - bool hasRun = false; - - InvokeAsync(mainThreadId, () => { - action(); - hasRun = true; - }); - - // Lock until the action has run - while(!hasRun) { - Thread.Sleep(5); - } - } - - - public void Upate() { - lock(_LockActions) { - while(_Actions.Count > 0) { - _Actions.Dequeue()(); - } - } - } - - - } -} \ No newline at end of file diff --git a/src/Utils/Utils.csproj b/src/Utils/Utils.csproj index 5b89510..aeaf239 100644 --- a/src/Utils/Utils.csproj +++ b/src/Utils/Utils.csproj @@ -71,7 +71,6 @@ - diff --git a/test/UnitTest/MapTest.cs b/test/UnitTest/MapTest.cs index 6501538..92c8129 100644 --- a/test/UnitTest/MapTest.cs +++ b/test/UnitTest/MapTest.cs @@ -8,15 +8,18 @@ namespace Mapbox.UnitTest { using System.Drawing; using Mapbox.Map; using NUnit.Framework; + using System.Threading; [TestFixture] internal class MapTest { + + private Mono.FileSource fs; private bool _TileLoadingFinished; private System.Collections.Generic.List _Tiles; + private object _LockTiles = new object(); private System.Collections.Generic.List _FailedTiles; - Threading.Dispatcher _Dispatcher = new Threading.Dispatcher(); - + private object _LockFailedTiles = new object(); private void Map_QueueEmpty(object sender, System.EventArgs e) { @@ -25,17 +28,17 @@ private void Map_QueueEmpty(object sender, System.EventArgs e) { private void MapVector_TileReceived(object sender, MapTileReceivedEventArgs e) { //System.Diagnostics.Debug.WriteLine("Map_TileReceived: {0}", e.Tile.Id); if(!string.IsNullOrWhiteSpace(e.Tile.Error)) { - _FailedTiles.Add(e.Tile); + lock(_LockFailedTiles) { _FailedTiles.Add(e.Tile); } } else { - _Tiles.Add(e.Tile); + lock(_LockTiles) { _Tiles.Add(e.Tile); } } } private void MapRaster_TileReceived(object sender, MapTileReceivedEventArgs e) { //System.Diagnostics.Debug.WriteLine("Map_TileReceived: {0}", e.Tile.Id); if(!string.IsNullOrWhiteSpace(e.Tile.Error)) { - _FailedTiles.Add(e.Tile); + lock(_LockFailedTiles) { _FailedTiles.Add(e.Tile); } } else { - _Tiles.Add(e.Tile); + lock(_LockTiles) { _Tiles.Add(e.Tile); } } } private void MapClassicRaster_TileReceived(object sender, MapTileReceivedEventArgs e) { @@ -58,8 +61,7 @@ public void SetUp() { public void World() { var map = new Map( - System.Threading.Thread.CurrentThread.ManagedThreadId - , this.fs + this.fs , 64 , 65 , 4 @@ -82,7 +84,6 @@ public void World() { //wait for all requests while(!_TileLoadingFinished) { - _Dispatcher.Upate(); System.Threading.Thread.Sleep(5); } @@ -102,8 +103,7 @@ public void World() { public void RasterHelsinki() { var map = new Map( - System.Threading.Thread.CurrentThread.ManagedThreadId - , this.fs + this.fs , 64 , 65 , 4 @@ -125,7 +125,6 @@ public void RasterHelsinki() { //wait for all requests while(!_TileLoadingFinished) { - _Dispatcher.Upate(); System.Threading.Thread.Sleep(5); } @@ -144,8 +143,7 @@ public void RasterHelsinki() { public void ChangeMapId() { var map = new Map( - System.Threading.Thread.CurrentThread.ManagedThreadId - , this.fs + this.fs , 64 , 65 , 4 @@ -168,7 +166,6 @@ public void ChangeMapId() { //wait for all requests while(!_TileLoadingFinished) { - _Dispatcher.Upate(); System.Threading.Thread.Sleep(5); } Assert.AreEqual(1, _FailedTiles.Count); @@ -182,7 +179,6 @@ public void ChangeMapId() { //wait for all requests while(!_TileLoadingFinished) { - _Dispatcher.Upate(); System.Threading.Thread.Sleep(5); } @@ -197,7 +193,6 @@ public void ChangeMapId() { //wait for all requests while(!_TileLoadingFinished) { - _Dispatcher.Upate(); System.Threading.Thread.Sleep(5); } @@ -214,8 +209,7 @@ public void ChangeMapId() { [Test, Timeout(8000)] public void Zoom() { var map = new Map( - System.Threading.Thread.CurrentThread.ManagedThreadId - , this.fs + this.fs , 64 , 65 , 4 diff --git a/test/UnitTest/VectorTileTest.cs b/test/UnitTest/VectorTileTest.cs index 4223df5..1966576 100644 --- a/test/UnitTest/VectorTileTest.cs +++ b/test/UnitTest/VectorTileTest.cs @@ -20,7 +20,6 @@ internal class VectorTileTest { private bool _TileLoadingFinished; private System.Collections.Generic.List _Tiles; private System.Collections.Generic.List _FailedTiles; - Threading.Dispatcher _Dispatcher = new Threading.Dispatcher(); private void Map_QueueEmpty(object sender, System.EventArgs e) { @@ -46,8 +45,7 @@ public void SetUp() { public void ParseSuccess() { var map = new Map( - System.Threading.Thread.CurrentThread.ManagedThreadId - , this.fs + this.fs , 15 , 16 , 4 @@ -73,7 +71,6 @@ public void ParseSuccess() { map.Zoom = zoom; //wait for all requests while(!_TileLoadingFinished) { - _Dispatcher.Upate(); System.Threading.Thread.Sleep(5); } } @@ -111,8 +108,7 @@ public void ParseFailure() { mockFs.SetReponse(resource.GetUrl(), response); var map = new Map( - System.Threading.Thread.CurrentThread.ManagedThreadId - , mockFs + mockFs , 1 , 2 , 4 @@ -134,10 +130,8 @@ public void ParseFailure() { map.Zoom = 13; - //mockFs.WaitForAllRequests(); //wait for all requests while(!_TileLoadingFinished) { - _Dispatcher.Upate(); System.Threading.Thread.Sleep(5); } @@ -155,8 +149,7 @@ public void ParseFailure() { public void SeveralTiles() { var map = new Map( - System.Threading.Thread.CurrentThread.ManagedThreadId - , this.fs + this.fs , 64 , 65 , 4 @@ -179,7 +172,6 @@ public void SeveralTiles() { map.Zoom = 3; // 64 tiles. while(!_TileLoadingFinished) { - _Dispatcher.Upate(); System.Threading.Thread.Sleep(5); }