using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Model { public abstract class LocationTask : SceneEntity { protected LocationTask() { } protected LocationTask(long id) : base(id) { } public abstract void Run(); } public sealed class LocationLockTask : LocationTask { private readonly string key; private readonly TaskCompletionSource tcs; public LocationLockTask(string key) { this.key = key; this.tcs = new TaskCompletionSource(); } public Task Task { get { return this.tcs.Task; } } public override void Run() { try { Scene.GetComponent().Lock(this.key); this.tcs.SetResult(true); } catch (Exception e) { this.tcs.SetException(e); } } } public sealed class LocationQueryTask : LocationTask { private readonly string key; private readonly TaskCompletionSource tcs; public LocationQueryTask(string key) { this.key = key; this.tcs = new TaskCompletionSource(); } public Task Task { get { return this.tcs.Task; } } public override void Run() { try { string location = Scene.GetComponent().Get(key); this.tcs.SetResult(location); } catch (Exception e) { this.tcs.SetException(e); } } } public class LocationComponent : Component { private readonly Dictionary locations = new Dictionary(); private readonly HashSet lockSet = new HashSet(); private readonly Dictionary> taskQueues = new Dictionary>(); public void Add(string key, string address) { this.locations[key] = address; } public void Remove(string key) { this.locations.Remove(key); } public string Get(string key) { this.locations.TryGetValue(key, out string location); return location; } public void Lock(string key) { if (this.lockSet.Contains(key)) { return; } this.lockSet.Add(key); } public void UnLock(string key) { this.lockSet.Remove(key); if (!this.taskQueues.TryGetValue(key, out Queue tasks)) { return; } while (true) { if (tasks.Count <= 0) { this.taskQueues.Remove(key); return; } if (this.lockSet.Contains(key)) { return; } LocationTask task = tasks.Dequeue(); task.Run(); } } public Task LockAsync(string key) { if (!this.lockSet.Contains(key)) { this.Lock(key); return Task.FromResult(true); } LocationLockTask task = new LocationLockTask(key); this.AddTask(key, task); return task.Task; } public Task GetAsync(string key) { if (!this.lockSet.Contains(key)) { this.locations.TryGetValue(key, out string location); return Task.FromResult(location); } LocationQueryTask task = new LocationQueryTask(key); this.AddTask(key, task); return task.Task; } public void AddTask(string key, LocationTask task) { if (!this.taskQueues.TryGetValue(key, out Queue tasks)) { tasks = new Queue(); this.taskQueues[key] = tasks; } task.Scene = this.GetOwner(); tasks.Enqueue(task); } public override void Dispose() { if (this.Id == 0) { return; } base.Dispose(); } } }