Пример #1
0
        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);
                }
            }
        }
Пример #2
0
        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);
            }
        }
Пример #3
0
        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);
            }
        }
Пример #4
0
        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);
            }
        }
Пример #5
0
        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
                    }
                }
            }
        }
Пример #6
0
        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);
        }
Пример #7
0
        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);
            }
        }
Пример #8
0
        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);
            }
        }
Пример #9
0
        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);
            }
        }
Пример #10
0
        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);
        }
Пример #11
0
        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);
            }
        }
Пример #12
0
        public void ReleaseLocalDb()
        {
            var lockInfo = new WriteLockInfo();

            try
            {
                Lock(lockInfo);

                if (_localDb == null)
                {
                    return;
                }
                _localDb.Dispose();
                _localDb = null;
            }
            finally
            {
                Release(lockInfo);
            }
        }
Пример #13
0
        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);
            }
        }
Пример #14
0
        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);
                }
            }
        }
Пример #15
0
        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);
                }
            }
        }
Пример #16
0
        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);
            }
        }
Пример #17
0
        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);
            }
        }
Пример #18
0
        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);
            }
        }
Пример #19
0
        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);
            }
        }
Пример #20
0
        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);
            }
        }