using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.IO; namespace Pithos.Core { public enum NetworkOperation { None, Uploading, Downloading } //The NetworkGate prevents starting download/uploads for files that are already in the process of downloading, //uploading. public class NetworkGate:IDisposable { public string FilePath { get; private set; } public NetworkOperation Operation { get; private set; } [ContractInvariantMethod] private void Invariants() { Contract.Invariant(!String.IsNullOrWhiteSpace(FilePath)); Contract.Invariant(Path.IsPathRooted(FilePath)); } //The state of each file is stored in a thread-safe dictionary static readonly ConcurrentDictionary NetworkState = new ConcurrentDictionary(); public static NetworkOperation GetNetworkState(string path) { if (String.IsNullOrWhiteSpace(path)) throw new ArgumentNullException("path"); if (!Path.IsPathRooted(path)) throw new ArgumentException("path must be a rooted path", "path"); Contract.EndContractBlock(); NetworkOperation operation; if (NetworkState.TryGetValue(path.ToLower(), out operation)) return operation; return NetworkOperation.None; } //Store a network operation for the specified path public static void SetNetworkState(string path, NetworkOperation operation) { if (String.IsNullOrWhiteSpace(path)) throw new ArgumentNullException("path"); if (!Path.IsPathRooted(path)) throw new ArgumentException("path must be a rooted path", "path"); Contract.EndContractBlock(); var lower = path.ToLower(); NetworkState[lower] = operation; //By default, None values don't need to be stored. They are stored anyway //because TryRemove may fail. if (operation == NetworkOperation.None) { NetworkOperation oldOperation; NetworkState.TryRemove(lower, out oldOperation); } } //Clients should acquire a NetworkGate before starting any network operation. //If Acquire fails, another network operation is already in progress public static NetworkGate Acquire(string path, NetworkOperation operation) { if (String.IsNullOrWhiteSpace(path)) throw new ArgumentNullException("path"); if (!Path.IsPathRooted(path)) throw new ArgumentException("path must be a rooted path", "path"); Contract.EndContractBlock(); var state = GetNetworkState(path); //If no operation is in progress, return a NetworkGate return (state == NetworkOperation.None) ? new NetworkGate(path, operation) //otherwise return a gate with Fail flagged : new NetworkGate(path, NetworkOperation.None); } private NetworkGate(string path,NetworkOperation operation) { if (String.IsNullOrWhiteSpace(path)) throw new ArgumentNullException("path"); if (!Path.IsPathRooted(path)) throw new ArgumentException("path must be rooted","path"); Contract.EndContractBlock(); Operation = operation; FilePath = path.ToLower(); //Skip dummy operations (those with Operation == None) if (Operation != NetworkOperation.None) //and store the file's operation SetNetworkState(FilePath, operation); } //A NetworkGate has Failed if its operation is None public bool Failed { get { return Operation == NetworkOperation.None; } } //Release a gate by setting the NetworkOperation to None public void Release() { //Skip Failed flags if (!Failed) //And reset the operation state for the file SetNetworkState(FilePath,NetworkOperation.None); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected void Dispose(bool disposing) { if (disposing) { Release(); } } } }