private void Lock(WriteLockInfo lockInfo, bool forceGen = false) { Monitor.Enter(_wlocko, ref lockInfo.Taken); var rtaken = false; try { Monitor.Enter(_rlocko, ref rtaken); // see SnapDictionary try { } finally { _wlocked++; lockInfo.Count = true; if (_nextGen == false || (forceGen && _wlocked == 1)) // if true already... ok to have "holes" in generation objects { // because we are changing things, a new generation // is created, which will trigger a new snapshot _nextGen = true; _liveGen += 1; } } } finally { if (rtaken) { Monitor.Exit(_rlocko); } } }
public void SetAll(IEnumerable <ContentNodeKit> kits) { var lockInfo = new WriteLockInfo(); try { Lock(lockInfo); ClearLocked(_contentNodes); ClearLocked(_contentRootNodes); // do NOT clear types else they are gone! //ClearLocked(_contentTypesById); //ClearLocked(_contentTypesByAlias); // skip missing parents & unbuildable kits - what else could we do? foreach (var kit in kits.Where(x => ParentExistsLocked(x) && BuildKit(x))) { SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); if (_localDb != null) { RegisterChange(kit.Node.Id, kit); } AddToParentLocked(kit.Node); _xmap[kit.Node.Uid] = kit.Node.Id; } } finally { Release(lockInfo); } }
public void Clear() { var lockInfo = new WriteLockInfo(); try { Lock(lockInfo); // this is safe only because we're write-locked foreach (var kvp in _items.Where(x => x.Value != null)) { if (kvp.Value.Gen < _liveGen) { var link = new LinkedNode <TValue>(null, _liveGen, kvp.Value); _items.TryUpdate(kvp.Key, link, kvp.Value); } else { kvp.Value.Value = null; } } } finally { Release(lockInfo); } }
public bool Clear(int id) { var lockInfo = new WriteLockInfo(); try { Lock(lockInfo); // try to find the content // if it is not there, nothing to do _contentNodes.TryGetValue(id, out LinkedNode <ContentNode> link); // else null if (link?.Value == null) { return(false); } var content = link.Value; _logger.Debug <ContentStore>("Clear content ID: {ContentId}", content.Id); // clear the entire branch ClearBranchLocked(content); // manage the tree RemoveFromParentLocked(content); return(true); } finally { Release(lockInfo); } }
private void Lock(WriteLockInfo lockInfo, bool forceGen = false) { if (Monitor.IsEntered(_wlocko)) { throw new InvalidOperationException("Recursive locks not allowed"); } Monitor.Enter(_wlocko, ref lockInfo.Taken); lock (_rlocko) { // assume everything in finally runs atomically // http://stackoverflow.com/questions/18501678/can-this-unexpected-behavior-of-prepareconstrainedregions-and-thread-abort-be-ex // http://joeduffyblog.com/2005/03/18/atomicity-and-asynchronous-exception-failures/ // http://joeduffyblog.com/2007/02/07/introducing-the-new-readerwriterlockslim-in-orcas/ // http://chabster.blogspot.fr/2013/12/readerwriterlockslim-fails-on-dual.html //RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { if (_nextGen == false || (forceGen)) { // because we are changing things, a new generation // is created, which will trigger a new snapshot if (_nextGen) { _genObjs.Enqueue(_genObj = new GenObj(_liveGen)); } _liveGen += 1; _nextGen = true; // this is the ONLY place where _nextGen becomes true } } } }
private void Release(WriteLockInfo lockInfo, bool commit = true) { // if the lock wasn't taken in the first place, do nothing if (!lockInfo.Taken) { return; } if (commit == false) { var rtaken = false; try { Monitor.Enter(_rlocko, ref rtaken); try { } finally { // forget about the temp. liveGen _nextGen = false; _liveGen -= 1; } } finally { if (rtaken) { Monitor.Exit(_rlocko); } } foreach (var item in _items) { var link = item.Value; if (link.Gen <= _liveGen) { continue; } var key = item.Key; if (link.Next == null) { _items.TryRemove(key, out link); } else { _items.TryUpdate(key, link.Next, link); } } } // decrement the lock count, if counting, then exit the lock if (lockInfo.Count) { _wlocked--; } Monitor.Exit(_wlocko); }
private void Release(WriteLockInfo lockInfo, bool commit = true) { if (commit == false) { var rtaken = false; try { Monitor.Enter(_rlocko, ref rtaken); try { } finally { _nextGen = false; _liveGen -= 1; } } finally { if (rtaken) { Monitor.Exit(_rlocko); } } Rollback(_contentNodes); Rollback(_contentRootNodes); Rollback(_contentTypesById); Rollback(_contentTypesByAlias); } else if (_localDb != null && _wchanges != null) { foreach (var change in _wchanges) { if (change.Value.IsNull) { _localDb.TryRemove(change.Key, out ContentNodeKit unused); } else { _localDb[change.Key] = change.Value; } } _wchanges = null; _localDb.Commit(); } if (lockInfo.Count) { _wlocked--; } if (lockInfo.Taken) { Monitor.Exit(_wlocko); } }
private void Release(WriteLockInfo lockInfo, bool commit = true) { if (commit == false) { var rtaken = false; try { Monitor.Enter(_rlocko, ref rtaken); try { } finally { _nextGen = false; _liveGen -= 1; } } finally { if (rtaken) { Monitor.Exit(_rlocko); } } foreach (var item in _items) { var link = item.Value; if (link.Gen <= _liveGen) { continue; } var key = item.Key; if (link.Next == null) { _items.TryRemove(key, out link); } else { _items.TryUpdate(key, link.Next, link); } } } if (lockInfo.Count) { _wlocked--; } if (lockInfo.Taken) { Monitor.Exit(_wlocko); } }
public void Set(TKey key, TValue value) { var lockInfo = new WriteLockInfo(); try { Lock(lockInfo); // this is safe only because we're write-locked var link = GetHead(key); if (link != null) { // already in the dict if (link.Gen != _liveGen) { // for an older gen - if value is different then insert a new // link for the new gen, with the new value if (link.Value != value) { _items.TryUpdate(key, new LinkedNode <TValue>(value, _liveGen, link), link); } } else { // for the live gen - we can fix the live gen - and remove it // if value is null and there's no next gen if (value == null && link.Next == null) { _items.TryRemove(key, out link); } else { link.Value = value; } } } else { _items.TryAdd(key, new LinkedNode <TValue>(value, _liveGen)); } } finally { Release(lockInfo); } }
private void Release(WriteLockInfo lockInfo, bool commit = true) { // if the lock wasn't taken in the first place, do nothing if (!lockInfo.Taken) { return; } if (commit == false) { lock (_rlocko) { try { } finally { // forget about the temp. liveGen _nextGen = false; _liveGen -= 1; } } foreach (var item in _items) { var link = item.Value; if (link.Gen <= _liveGen) { continue; } var key = item.Key; if (link.Next == null) { _items.TryRemove(key, out link); } else { _items.TryUpdate(key, link.Next, link); } } } // TODO: Shouldn't this be in a finally block? Monitor.Exit(_wlocko); }
public void NewContentTypes(IEnumerable <PublishedContentType> types) { var lockInfo = new WriteLockInfo(); try { Lock(lockInfo); foreach (var type in types) { SetValueLocked(_contentTypesById, type.Id, type); SetValueLocked(_contentTypesByAlias, type.Alias, type); } } finally { Release(lockInfo); } }
public void ReleaseLocalDb() { var lockInfo = new WriteLockInfo(); try { Lock(lockInfo); if (_localDb == null) { return; } _localDb.Dispose(); _localDb = null; } finally { Release(lockInfo); } }
public void SetBranch(int rootContentId, IEnumerable <ContentNodeKit> kits) { var lockInfo = new WriteLockInfo(); try { Lock(lockInfo); // get existing _contentNodes.TryGetValue(rootContentId, out LinkedNode <ContentNode> link); var existing = link?.Value; // clear if (existing != null) { ClearBranchLocked(existing); RemoveFromParentLocked(existing); } // now add them all back // skip missing parents & unbuildable kits - what else could we do? foreach (var kit in kits.Where(x => ParentExistsLocked(x) && BuildKit(x))) { SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); if (_localDb != null) { RegisterChange(kit.Node.Id, kit); } AddToParentLocked(kit.Node); _xmap[kit.Node.Uid] = kit.Node.Id; } } finally { Release(lockInfo); } }
private void Lock(WriteLockInfo lockInfo, bool forceGen = false) { Monitor.Enter(_wlocko, ref lockInfo.Taken); var rtaken = false; try { Monitor.Enter(_rlocko, ref rtaken); // assume everything in finally runs atomically // http://stackoverflow.com/questions/18501678/can-this-unexpected-behavior-of-prepareconstrainedregions-and-thread-abort-be-ex // http://joeduffyblog.com/2005/03/18/atomicity-and-asynchronous-exception-failures/ // http://joeduffyblog.com/2007/02/07/introducing-the-new-readerwriterlockslim-in-orcas/ // http://chabster.blogspot.fr/2013/12/readerwriterlockslim-fails-on-dual.html //RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { _wlocked++; lockInfo.Count = true; if (_nextGen == false || (forceGen && _wlocked == 1)) // if true already... ok to have "holes" in generation objects { // because we are changing things, a new generation // is created, which will trigger a new snapshot _nextGen = true; _liveGen += 1; } } } finally { if (rtaken) { Monitor.Exit(_rlocko); } } }
private void Lock(WriteLockInfo lockInfo, bool forceGen = false) { Monitor.Enter(_wlocko, ref lockInfo.Taken); var rtaken = false; try { Monitor.Enter(_rlocko, ref rtaken); // see SnapDictionary try { } finally { _wlocked++; lockInfo.Count = true; if (_nextGen == false || (forceGen && _wlocked == 1)) { // because we are changing things, a new generation // is created, which will trigger a new snapshot if (_nextGen) { _genObjs.Enqueue(_genObj = new GenObj(_liveGen)); } _liveGen += 1; _nextGen = true; } } } finally { if (rtaken) { Monitor.Exit(_rlocko); } } }
public void UpdateContentTypes(IEnumerable <PublishedContentType> types) { var lockInfo = new WriteLockInfo(); try { Lock(lockInfo); var index = types.ToDictionary(x => x.Id, x => x); foreach (var type in index.Values) { SetValueLocked(_contentTypesById, type.Id, type); SetValueLocked(_contentTypesByAlias, type.Alias, type); } foreach (var link in _contentNodes.Values) { var node = link.Value; if (node == null) { continue; } var contentTypeId = node.ContentType.Id; if (index.TryGetValue(contentTypeId, out PublishedContentType contentType) == false) { continue; } SetValueLocked(_contentNodes, node.Id, new ContentNode(node, contentType, _publishedSnapshotAccessor, _variationContextAccessor)); } } finally { Release(lockInfo); } }
public void UpdateContentTypes(IEnumerable <int> removedIds, IEnumerable <PublishedContentType> refreshedTypes, IEnumerable <ContentNodeKit> kits) { var removedIdsA = removedIds?.ToArray() ?? Array.Empty <int>(); var refreshedTypesA = refreshedTypes?.ToArray() ?? Array.Empty <PublishedContentType>(); var refreshedIdsA = refreshedTypesA.Select(x => x.Id).ToArray(); kits = kits ?? Array.Empty <ContentNodeKit>(); var lockInfo = new WriteLockInfo(); try { Lock(lockInfo); var removedContentTypeNodes = new List <int>(); var refreshedContentTypeNodes = new List <int>(); foreach (var link in _contentNodes.Values) { var node = link.Value; if (node == null) { continue; } var contentTypeId = node.ContentType.Id; if (removedIdsA.Contains(contentTypeId)) { removedContentTypeNodes.Add(node.Id); } if (refreshedIdsA.Contains(contentTypeId)) { refreshedContentTypeNodes.Add(node.Id); } } // all content should have been deleted - but foreach (var node in removedContentTypeNodes) { ClearBranchLocked(node); } foreach (var id in removedIdsA) { if (_contentTypesById.TryGetValue(id, out LinkedNode <PublishedContentType> link) == false || link.Value == null) { continue; } SetValueLocked(_contentTypesById, id, null); SetValueLocked(_contentTypesByAlias, link.Value.Alias, null); } foreach (var type in refreshedTypesA) { SetValueLocked(_contentTypesById, type.Id, type); SetValueLocked(_contentTypesByAlias, type.Alias, type); } // skip missing type // skip missing parents & unbuildable kits - what else could we do? var visited = new List <int>(); foreach (var kit in kits.Where(x => refreshedIdsA.Contains(x.ContentTypeId) && ParentExistsLocked(x) && BuildKit(x))) { SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); visited.Add(kit.Node.Id); if (_localDb != null) { RegisterChange(kit.Node.Id, kit); } } // all content should have been refreshed - but... var orphans = refreshedContentTypeNodes.Except(visited); foreach (var id in orphans) { ClearBranchLocked(id); } } finally { Release(lockInfo); } }
public void UpdateDataTypes(IEnumerable <int> dataTypeIds, Func <int, PublishedContentType> getContentType) { var lockInfo = new WriteLockInfo(); try { Lock(lockInfo); var contentTypes = _contentTypesById .Where(kvp => kvp.Value.Value != null && kvp.Value.Value.PropertyTypes.Any(p => dataTypeIds.Contains(p.DataType.Id))) .Select(kvp => kvp.Value.Value) .Select(x => getContentType(x.Id)) .Where(x => x != null) // poof, gone, very unlikely and probably an anomaly .ToArray(); var contentTypeIdsA = contentTypes.Select(x => x.Id).ToArray(); var contentTypeNodes = new Dictionary <int, List <int> >(); foreach (var id in contentTypeIdsA) { contentTypeNodes[id] = new List <int>(); } foreach (var link in _contentNodes.Values) { var node = link.Value; if (node != null && contentTypeIdsA.Contains(node.ContentType.Id)) { contentTypeNodes[node.ContentType.Id].Add(node.Id); } } foreach (var contentType in contentTypes) { // again, weird situation if (contentTypeNodes.ContainsKey(contentType.Id) == false) { continue; } foreach (var id in contentTypeNodes[contentType.Id]) { _contentNodes.TryGetValue(id, out LinkedNode <ContentNode> link); if (link?.Value == null) { continue; } var node = new ContentNode(link.Value, contentType, _publishedSnapshotAccessor, _variationContextAccessor); SetValueLocked(_contentNodes, id, node); if (_localDb != null) { RegisterChange(id, node.ToKit()); } } } } finally { Release(lockInfo); } }
public void Set(ContentNodeKit kit) { // ReSharper disable LocalizableElement if (kit.IsEmpty) { throw new ArgumentException("Kit is empty.", nameof(kit)); } if (kit.Node.ChildContentIds.Count > 0) { throw new ArgumentException("Kit content cannot have children.", nameof(kit)); } // ReSharper restore LocalizableElement _logger.Debug <ContentStore>("Set content ID: {KitNodeId}", kit.Node.Id); var lockInfo = new WriteLockInfo(); try { Lock(lockInfo); // get existing _contentNodes.TryGetValue(kit.Node.Id, out LinkedNode <ContentNode> link); var existing = link?.Value; // else ignore, what else could we do? if (ParentExistsLocked(kit) == false || BuildKit(kit) == false) { return; } // moving? var moving = existing != null && existing.ParentContentId != kit.Node.ParentContentId; // manage children if (existing != null) { kit.Node.ChildContentIds = existing.ChildContentIds; } // set SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); if (_localDb != null) { RegisterChange(kit.Node.Id, kit); } // manage the tree if (existing == null) { // new, add to parent AddToParentLocked(kit.Node); } else if (moving) { // moved, remove existing from its parent, add content to its parent RemoveFromParentLocked(existing); AddToParentLocked(kit.Node); } _xmap[kit.Node.Uid] = kit.Node.Id; } finally { Release(lockInfo); } }
public void UpdateContentTypes(IEnumerable <int> removedIds, IEnumerable <PublishedContentType> refreshedTypes, IEnumerable <ContentNodeKit> kits) { var removedIdsA = removedIds?.ToArray() ?? Array.Empty <int>(); var refreshedTypesA = refreshedTypes?.ToArray() ?? Array.Empty <PublishedContentType>(); var refreshedIdsA = refreshedTypesA.Select(x => x.Id).ToArray(); kits = kits ?? Array.Empty <ContentNodeKit>(); var lockInfo = new WriteLockInfo(); try { Lock(lockInfo); var removedContentTypeNodes = new List <int>(); var refreshedContentTypeNodes = new List <int>(); // find all the nodes that are either refreshed or removed, // because of their content type being either refreshed or removed foreach (var link in _contentNodes.Values) { var node = link.Value; if (node == null) { continue; } var contentTypeId = node.ContentType.Id; if (removedIdsA.Contains(contentTypeId)) { removedContentTypeNodes.Add(node.Id); } if (refreshedIdsA.Contains(contentTypeId)) { refreshedContentTypeNodes.Add(node.Id); } } // perform deletion of content with removed content type // removing content types should have removed their content already // but just to be 100% sure, clear again here foreach (var node in removedContentTypeNodes) { ClearBranchLocked(node); } // perform deletion of removed content types foreach (var id in removedIdsA) { if (_contentTypesById.TryGetValue(id, out var link) == false || link.Value == null) { continue; } SetValueLocked(_contentTypesById, id, null); SetValueLocked(_contentTypesByAlias, link.Value.Alias, null); } // perform update of refreshed content types foreach (var type in refreshedTypesA) { SetValueLocked(_contentTypesById, type.Id, type); SetValueLocked(_contentTypesByAlias, type.Alias, type); } // perform update of content with refreshed content type - from the kits // skip missing type, skip missing parents & unbuildable kits - what else could we do? // kits are ordered by level, so ParentExits is ok here var visited = new List <int>(); foreach (var kit in kits.Where(x => refreshedIdsA.Contains(x.ContentTypeId) && ParentExistsLocked(x) && BuildKit(x))) { // replacing the node: must preserve the parents var node = GetHead(_contentNodes, kit.Node.Id)?.Value; if (node != null) { kit.Node.ChildContentIds = node.ChildContentIds; } SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); visited.Add(kit.Node.Id); if (_localDb != null) { RegisterChange(kit.Node.Id, kit); } } // all content should have been refreshed - but... var orphans = refreshedContentTypeNodes.Except(visited); foreach (var id in orphans) { ClearBranchLocked(id); } } finally { Release(lockInfo); } }