public virtual (IReplica Replica, bool IsNew) GetOrRegister(PublicationRef publicationRef, Func <IReplica> replicaFactory) { var random = publicationRef.PublicationId.HashCode; OnOperation(random); var spinWait = new SpinWait(); var newReplica = (IReplica?)null; // Just to make sure we store this ref while (true) { // ReSharper disable once HeapView.CanAvoidClosure var handle = _handles.GetOrAdd(publicationRef, _ => { newReplica = replicaFactory.Invoke(); return(_gcHandlePool.Acquire(newReplica, random)); }); var target = (IReplica?)handle.Target; if (target != null) { if (target == newReplica) { return(target, true); } (newReplica as IReplicaImpl)?.DisposeTemporaryReplica(); return(target, false); } // GCHandle target == null => we have to recycle it if (_handles.TryRemove(publicationRef, handle)) { // The thread that succeeds in removal releases gcHandle as well _gcHandlePool.Release(handle, random); } // And since we didn't manage to add the replica, let's retry spinWait.SpinOnce(); } }
public virtual void Register(IComputed computed) { // Debug.WriteLine($"{nameof(Register)}: {computed}"); var key = computed.Input; var random = Randomize(key.HashCode); OnOperation(random); var spinWait = new SpinWait(); GCHandle?newHandle = null; while (computed.ConsistencyState != ConsistencyState.Invalidated) { if (_storage.TryGetValue(key, out var handle)) { var target = (IComputed?)handle.Target; if (target == computed) { break; } if (target == null || target.ConsistencyState == ConsistencyState.Invalidated) { if (_storage.TryRemove(key, handle)) { _gcHandlePool.Release(handle, random); } } else { // This typically triggers Unregister - // except for ReplicaClientComputed. target.Invalidate(); } } else { newHandle ??= _gcHandlePool.Acquire(computed, random); if (_storage.TryAdd(key, newHandle.GetValueOrDefault())) { if (computed.ConsistencyState == ConsistencyState.Invalidated) { if (_storage.TryRemove(key, handle)) { _gcHandlePool.Release(handle, random); } } break; } } spinWait.SpinOnce(); } }