private void Release(ReadLockInfo lockInfo) { if (lockInfo.Taken) { Monitor.Exit(_rlocko); } }
public Snapshot CreateSnapshot() { var lockInfo = new ReadLockInfo(); try { Lock(lockInfo); // if no next generation is required, and we already have a gen object, // use it to create a new snapshot if (_nextGen == false && _genObj != null) { return(new Snapshot(this, _genObj.GetGenRef())); } // else we need to try to create a new gen object // whether we are wlocked or not, noone can rlock while we do, // so _liveGen and _nextGen are safe if (_wlocked > 0) // volatile, cannot ++ but could -- { // write-locked, cannot use latest gen (at least 1) so use previous var snapGen = _nextGen ? _liveGen - 1 : _liveGen; // create a new gen object if we don't already have one // (happens the first time a snapshot is created) if (_genObj == null) { _genObjs.Enqueue(_genObj = new GenObj(snapGen)); } // if we have one already, ensure it's consistent else if (_genObj.Gen != snapGen) { throw new PanicException($"The generation {_genObj.Gen} does not equal the snapshot generation {snapGen}"); } } else { // not write-locked, can use latest gen (_liveGen), create a corresponding new gen object _genObjs.Enqueue(_genObj = new GenObj(_liveGen)); _nextGen = false; // this is the ONLY thing that triggers a _liveGen++ } // so... // the genObj has a weak ref to the genRef, and is queued // the snapshot has a ref to the genRef, which has a ref to the genObj // when the snapshot is disposed, it decreases genObj counter // so after a while, one of these conditions is going to be true: // - genObj.Count is zero because all snapshots have properly been disposed // - genObj.WeakGenRef is dead because all snapshots have been collected // in both cases, we will dequeue and collect var snapshot = new Snapshot(this, _genObj.GetGenRef()); // reading _floorGen is safe if _collectTask is null if (_collectTask == null && _collectAuto && _liveGen - _floorGen > CollectMinGenDelta) { CollectAsyncLocked(); } return(snapshot); } finally { Release(lockInfo); } }
private void Lock(ReadLockInfo lockInfo) { Monitor.Enter(_rlocko, ref lockInfo.Taken); }
public Snapshot CreateSnapshot() { var lockInfo = new ReadLockInfo(); try { Lock(lockInfo); // if no next generation is required, and we already have one, // use it and create a new snapshot if (_nextGen == false && _generationObject != null) { return(new Snapshot(this, _generationObject.GetReference())); } // else we need to try to create a new gen ref // whether we are wlocked or not, noone can rlock while we do, // so _liveGen and _nextGen are safe if (_wlocked > 0) // volatile, cannot ++ but could -- { // write-locked, cannot use latest gen (at least 1) so use previous var snapGen = _nextGen ? _liveGen - 1 : _liveGen; // create a new gen ref unless we already have it if (_generationObject == null) { _generationObjects.Enqueue(_generationObject = new GenerationObject(snapGen)); } else if (_generationObject.Gen != snapGen) { throw new Exception("panic"); } } else { // not write-locked, can use latest gen, create a new gen ref _generationObjects.Enqueue(_generationObject = new GenerationObject(_liveGen)); _nextGen = false; // this is the ONLY thing that triggers a _liveGen++ } // so... // the genRefRef has a weak ref to the genRef, and is queued // the snapshot has a ref to the genRef, which has a ref to the genRefRef // when the snapshot is disposed, it decreases genRefRef counter // so after a while, one of these conditions is going to be true: // - the genRefRef counter is zero because all snapshots have properly been disposed // - the genRefRef weak ref is dead because all snapshots have been collected // in both cases, we will dequeue and collect var snapshot = new Snapshot(this, _generationObject.GetReference()); // reading _floorGen is safe if _collectTask is null if (_collectTask == null && _collectAuto && _liveGen - _floorGen > CollectMinGenDelta) { CollectAsyncLocked(); } return(snapshot); } finally { Release(lockInfo); } }