/// <summary> /// Declares the key is going to be computed. /// It blocks the caller if there is another thread computing that key. When that other thread finishes, /// this thread is released and returned false. the caller is expected to get the item from the cache /// then (and caller must assume the object may be removed -unlikely but possible- by the time the caller tries to retrieve it). /// If the method returns true, the caller to invoke "Complete" once it adds the element to the cache. /// </summary> /// <param name="key">The key.</param> /// <returns><c>true</c> if true, the key doesn't have a value and is not being computed yet<c>false</c> otherwise.</returns> public bool StartInflight(TKey key) { EventAndCount ev = null; lock (this.inflight) { if (!this.inflight.TryGetValue(key, out ev)) { this.inflight.Add(key, null); } else { if (ev == null) { ev = new EventAndCount(); this.inflight[key] = ev; } } } if (ev == null) { return(true); } ev.WaitAndReturnIfLast(); return(false); }
/// <summary> /// Completes the inflight operation to the specified key. /// This method needs to be invoked right after the Add or TryAdd call if the caller invoked "Inflight". /// </summary> /// <param name="key">The key just added.</param> /// <exception cref="System.InvalidOperationException">Complete was invoked without a corresponding Inflight call</exception> public void CompleteInflight(TKey key) { lock (this.inflight) { EventAndCount ev = null; if (!this.inflight.TryGetValue(key, out ev)) { throw new InvalidOperationException("Complete was invoked without a corresponding Inflight call"); } this.inflight.Remove(key); // if anybody was waiting on this, now we tell them. ev?.Set(); } }