public string GetData(StreamData stream, int start, int count = -1) { lock (stream.Lock) { if (start < 0) { throw new InvalidOperationException("Start less than zero!"); } //I don't care what the inputs are, if the stream is empty, you can have it if (stream.Data.Length == 0) { return(""); } if (start >= stream.Data.Length) { throw new InvalidOperationException($"Start beyond end of data: {stream.Data.Length}!"); } if (count < 0 || count > stream.Data.Length - start) { count = stream.Data.Length - start; } return(stream.Data.ToString(start, count)); } }
/// <summary> /// Get an existing stream from memory. If it's not in memory, check disk. If it's not on disk, create a new one. /// In any case, at the end of this call, a stream is guaranteed to be in memory. /// </summary> /// <param name="name"></param> /// <returns></returns> public StreamData GetStream(string name) { //Don't let ANYBODY else mess with the dictionary while we're doing it! lock (Lock) { if (!Streams.ContainsKey(name)) { //Look for the stream in permament storage. var existing = LoadStream(name); //Oops it didn't exist, set to a new one if (existing == null) { logger.LogInformation($"Creating new room {name}"); existing = new StreamData(); } else { //Just log that it was found logger.LogInformation($"Reviving dead room {name}"); } Streams.Add(name, existing); } //NOTE: because we don't get rid of readonly key associations, we ASSUME that if //we have an existing stream, there IS a readonly key for it! return(Streams[name]); } }
protected void SaveStream(string name, StreamData s, bool forceEmpty = false) { if (!Directory.Exists(Config.StoreLocation)) { Directory.CreateDirectory(Config.StoreLocation); } //Don't save empty rooms (unless we're forced to!) if (s.Data.Length > 0 || forceEmpty) { File.WriteAllText(Path.Combine(Config.StoreLocation, name), s.Data.ToString()); } s.SaveDate = DateTime.Now; }
public async Task <ReadyData> GetDataWhenReady(StreamData stream, int start, int count = -1) { //JUST IN CASE we need it later (can't make it in the lock section, needed outside!) bool completed = false; StreamListener listener = null; ReadyData result = new ReadyData(); lock (stream.Lock) { if (start < stream.Data.Length) { //No waiting! We're already done! The stream can never get smaller! completed = true; } else { //Oh, waiting... we're a new listener so add it! listener = new StreamListener(); listener.Waiter = Task.Run(() => completed = stream.Signal.WaitOne(Config.ListenTimeout)); stream.Listeners.Add(listener); } } //Don't need to listen if there's no listener! if (listener != null) { //CANNOT wait in the lock! We're just waiting to see if we get data. If we DOOOO, "completed" will be true! try { await listener.Waiter; } finally { //We're done. Doesn't matter what happened, whether it finished or we threw an exception, //we are NO LONGER listening! result.SignalData = listener.SignalData; //this might be nothing stream.Listeners.Remove(listener); } } if (completed) { result.Data = GetData(stream, start, count); } return(result); }
public void AddData(StreamData stream, string data) { lock (stream.Lock) { if (data.Length == 0) { throw new InvalidOperationException("Can't add 0 length data!"); } if (data.Length > Config.SingleDataLimit) { throw new InvalidOperationException($"Too much data at once!: {Config.SingleDataLimit}"); } //Don't allow data additions that would allow the limit to go beyond thingy! if (stream.Data.Length >= Config.StreamDataLimit) { throw new InvalidOperationException($"Stream at data limit: {Config.StreamDataLimit}"); } stream.Data.Append(data); stream.UpdateDate = DateTime.Now; var signalData = new SignalData() { ListenersBeforeSignal = stream.Listeners.Count }; //Set the signal data before signalling so they have "communication" from us. NOT SAFE //since someone could... you know, add a listener... or could they? We hold the lock for //the stream and you can't add a listener without the lock! So probably safe... stream.Listeners.ForEach(x => x.SignalData = signalData); //Set the signal so all the listeners know they have data! stream.Signal.Set(); try { var signalStart = DateTime.Now; //Wait for OUR listeners to clear out! Notice that the listener wait and removal is NOT //in a lock: this allows US to hold the lock (since it's probably safer...? we're doing the signalling). while (stream.Listeners.Count > 0) { System.Threading.Thread.Sleep(Config.SignalWaitInterval); if (DateTime.Now - signalStart > Config.SignalTimeout) { logger.LogWarning("Timed out while waiting for listeners to process signal!"); break; } } } finally { //ALWAYS get rid of listeners and reset the signal! we don't want to be left in an unknown state! stream.Listeners.Clear(); //This might be dangerous? IDK, we don't want to wait forever! stream.Signal.Reset(); } } }