Пример #1
0
            public InternalVaultExposition(VaultBase vault)
            {
                _vault = vault;
                PrevRecursiveExpositionsInternal = _internalLock.RecursiveReadCount;

                _lock.EnterReadLock();
                _internalLock.EnterReadLock();
            }
Пример #2
0
        protected Element(VaultBase vault, String name, IEnumerable <Element> children)
        {
            Vault = vault.AssertNotNull();
            Bind(Vault);

            // setting parent auto-adds the child to this collection
            // so it should be initialized as an empty one
            _children = new IndexedNodeCollection(this);
            (children ?? Enumerable.Empty <Element>()).ForEach(c => c.Parent = (Branch)this);

            Name      = name;
            _metadata = new Metadata(this);
        }
Пример #3
0
        public void Bind(VaultBase vault)
        {
            using (Vault.ExposeReadWrite())
            {
                VerifyMutation(VPath);

                if (BoundVault == vault)
                {
                    return;
                }
                if (BoundVault != null)
                {
                    Unbind();
                }
                BoundVault = vault;

                vault.Bind(this);
            }
        }
Пример #4
0
            public VaultExposition(VaultBase vault, bool exposeReadOnly)
            {
                _vault = vault;
                _thisExpositionIsReadOnly  = exposeReadOnly;
                PrevRecursiveExpositionsRO = _lock.RecursiveReadCount;
                PrevRecursiveExpositionsRW = _lock.RecursiveWriteCount;

                if (exposeReadOnly)
                {
                    if (_lock.IsWriteLockHeld)
                    {
                        // do nothing - holding a write lock allows us to do any reading
                    }
                    else
                    {
                        _lock.EnterReadLock();
                    }
                }
                else
                {
                    // letting read -> write upgrade to be an atomic operation would make us prone to deadlocks
                    // consider the following example:
                    // thread 1: [exposeRO -> [exposeRW (will wait for t2 to release exposeRO) => locked
                    // thread 2: [exposeRO -> [exposeRW (needs to perform exposeRW in order to quit the using exposeRO block) => locked

                    // so the implementation used below is not randomly coded up, but rather after some considerations
                    _readLocksAbandoned = _lock.RecursiveReadCount;
                    while (_lock.RecursiveReadCount != 0)
                    {
                        _lock.ExitReadLock();
                    }
                    _lock.EnterWriteLock();

                    // yeah, I'm aware of the danger of such implementation quirk
                    // for a split moment in-between read -> write transition, or, reverse during the write -> read transition
                    // the thread totally loses control of the lock, and an alient thread might hijack the lock just at the moment
                    // when read locks are abandoned but the write lock hasn't yet been acquired (or the reverse)

                    // note. the code under danger in this situation
                    // is the one that features split r/w expositions within an operation encapsulated in a r/o lock
                    // an author could assume that when protecting the whole op with a r/o lock, he/she excludes the possibility
                    // of someone writing into the vault for the entire duration of the whole op.
                    // however, due to implementation details this is untrue, and there's a possibility for another thread to interpose
                    // and change vault state in-between seemingly atomary operation.

                    // to fix this trouble, it'd be cool to make every holder of a readonly lock temporarily lose their privileges
                    // in favor of current thread which then freely upgrades to read/write. the following things need to be implemented then:
                    // 1) when r/w request is issued, lock the lock, stop r/o threads from accessing the vault
                    // note. ah, damn this won't work, since at that time alien threads might suddenly find out the vault changed

                    // however after some thought we can conclude that dangerous scenarios made possible by this implementation quirk
                    // are limited to the situation described above. and here's why:
                    // * if the rule described in the n0te above is fulfilled, then no unexpected stuff might happen due to hijacking
                    //   and the only threat is leaving vault in inconsistent state
                    // * the vault might be in inconsistent state only when it's r/w-exposed
                    // * thus, if a thread is in read -> write transition, or, reverse, in the write -> read transition
                    //   it can at max have the r/o-exposition active, i.e. (if the code is designed correctly) at that moment
                    //   the vault is in consistent state, so it will be after the hijacker finishes (and before that the original
                    //   thread would be unable to acquire it's lock)
                    // note. it would be nice if the stuff above worked, but it's untrue
                    // counter-example: the "this[key]" accessor of some map exposes it as r/o, and checks whether the key/value is present
                    // then when it makes sure that the r/w is necessary, the code exposes the map instance as r/w, and adds the new kvp.
                    // error scenario: during the r/o -> r/w transition, an alien thread manages to hijack the lock and write some kvp
                    // that has the same key as the one being added by an original thread. shortly after, the original crashes when attempting
                    // to add the duplicate key to the map.

                    // preventing such situations is a sole responsibility of DataVault users that should use a rule:
                    // note. if code block A depends on the results of code block B, then the most enclosing r/w exposition for both A and B must be the same
                    // examples:
                    // 1) r/o > r/w > { a(); b(); } is fine
                    // 2) r/o > { a(); r/w > b(); } fails the rule (see the map-related synchronization example above)
                    // 3) r/o > { r/w > a(); r/w > b(); } fails the rule as well

                    // some code of this codebase violates this principles in favor of robust Revision tracking
                    // (e.g. Rename first exposes the vault for R/O, then checks the necessity of a modification, and only then uses R/W)
                    // todo. fix the described issue through a careful code-review and replacing read acquisitions with upgradedable reads
                }
            }
Пример #5
0
 public Branch(VaultBase vault, String name, IEnumerable <Element> children)
     : base(vault, name, children)
 {
 }
Пример #6
0
 public Value(VaultBase vault, String name, Func <Stream> content)
     : base(vault, name, null)
 {
     _content = content ?? (() => null);
 }