This class handles everything about assigning version stamps to readers and writers, and takes care of determining what is the minimum used stamp, so that old unreachable versions can be released to the GC.
コード例 #1
0
ファイル: Shield.cs プロジェクト: lanicon/Shielded
        /// <summary>
        /// Executes the commutes, returning through the out param the set of items that the
        /// commutes had accessed.
        /// Increases the current start stamp, and leaves the commuted items unmerged with the
        /// main transaction items!
        /// </summary>
        static void RunCommutes(out TransItems commutedItems)
        {
            var ctx      = _context;
            var oldItems = ctx.Items;
            var commutes = oldItems.Commutes;

            Shield._blockCommute = true;
            try
            {
                while (true)
                {
                    ctx.ReadTicket = VersionList.GetUntrackedReadStamp();
                    ctx.Items      = commutedItems = new TransItems();
                    try
                    {
                        commutes.ForEach(comm => comm.Perform());
                        return;
                    }
                    catch (TransException)
                    {
                        commutedItems.Enlisted.Rollback();
                        commutedItems = null;
                    }
                }
            }
            finally
            {
                ctx.Items            = oldItems;
                Shield._blockCommute = false;
            }
        }
コード例 #2
0
ファイル: Shield.cs プロジェクト: lanicon/Shielded
 public void DoCommit()
 {
     Items.SyncFx.SafeRun();
     if (Items.HasChanges)
     {
         CommittingSubscription.Fire(Items);
         CommitWChanges();
     }
     else
     {
         CommitWoChanges();
     }
     VersionList.TrimCopies();
 }
コード例 #3
0
ファイル: Shield.cs プロジェクト: lanicon/Shielded
 private void Complete(bool committed)
 {
     try { }
     finally
     {
         if (WriteTicket != null && WriteTicket.Changes == null)
         {
             WriteTicket.Changes = EmptyChanges;
         }
         if (ReadTicket != null)
         {
             VersionList.ReleaseReaderTicket(ref ReadTicket);
         }
         if (WriteStamp != null && WriteStamp.Locked)
         {
             WriteStamp.Release();
         }
         Committed       = committed;
         Completed       = true;
         Shield._context = null;
     }
 }
コード例 #4
0
ファイル: Shield.cs プロジェクト: lanicon/Shielded
 public void Open()
 {
     VersionList.GetReaderTicket(out ReadTicket);
 }
コード例 #5
0
ファイル: Shield.cs プロジェクト: lanicon/Shielded
        /// <summary>
        /// Performs the commit check, returning the outcome. Prepares the write ticket in the context.
        /// The ticket is obtained before leaving the lock here, so that any thread which
        /// later conflicts with us will surely (in its retry) read the version we are now
        /// writing. The idea is to avoid senseless repetitions, retries after which a
        /// thread would again read old data and be doomed to fail again.
        /// It is critical that this ticket be marked when complete (i.e. Changes set to
        /// something non-null), because trimming will not go past it until this happens.
        /// </summary>
        static bool CommitCheck()
        {
            var ctx   = _context;
            var items = ctx.Items;

            if (!items.HasChanges)
            {
                return(true);
            }

            TransItems commutedItems   = null;
            var        oldReadTicket   = ctx.ReadTicket;
            bool       commit          = false;
            bool       brokeInCommutes = items.Commutes != null && items.Commutes.Count > 0;

            if (CommitSubscriptionContext.PreCommit.Count > 0)
            {
                // if any commute would trigger a pre-commit check, this check could, if executed
                // in the commute sub-transaction, see newer values in fields which
                // were read (but not written to) by the main transaction. commutes are normally
                // very isolated to prevent this, but pre-commits we cannot isolate.
                // so, commutes trigger them now, and they cause the commutes to degenerate.
                CommitSubscriptionContext.PreCommit
                .Trigger(brokeInCommutes ?
                         items.Enlisted.Where(HasChanges).Concat(
                             items.Commutes.SelectMany(c => c.Affecting)) :
                         items.Enlisted.Where(HasChanges))
                .Run();
                // in case a new commute sub was made
                brokeInCommutes = items.Commutes != null && items.Commutes.Count > 0;
            }

            try
            {
                repeatCommutes : if (brokeInCommutes)
                {
                    RunCommutes(out commutedItems);
#if DEBUG
                    if (items.Enlisted.Overlaps(commutedItems.Enlisted))
                    {
                        throw new InvalidOperationException("Invalid commute - conflict with transaction.");
                    }
#endif
                }

                var writeStamp = ctx.WriteStamp = new WriteStamp(ctx);
                lock (_checkLock)
                {
                    try
                    {
                        if (brokeInCommutes)
                        {
                            if (!commutedItems.Enlisted.CanCommit(writeStamp))
                            {
                                goto repeatCommutes;
                            }
                        }

                        ctx.ReadTicket  = oldReadTicket;
                        brokeInCommutes = false;
                        if (!items.Enlisted.CanCommit(writeStamp))
                        {
                            return(false);
                        }

                        commit = true;
                    }
                    finally
                    {
                        if (!commit)
                        {
                            if (commutedItems != null)
                            {
                                commutedItems.Enlisted.Rollback();
                            }
                            if (!brokeInCommutes)
                            {
                                items.Enlisted.Rollback();
                            }
                        }
                        else
                        {
                            VersionList.NewVersion(writeStamp, out ctx.WriteTicket);
                        }
                    }
                }
                return(true);
            }
            finally
            {
                ctx.ReadTicket      = oldReadTicket;
                ctx.CommitCheckDone = true;
                // note that this changes the _localItems.Enlisted hashset to contain the
                // commute-enlists as well, regardless of the check outcome.
                if (commutedItems != null)
                {
                    items.UnionWith(commutedItems);
                }
            }
        }