Ejemplo n.º 1
0
 /// <summary>
 /// Unions the other items into this. Does not include commutes!
 /// </summary>
 public void UnionWith(TransItems other)
 {
     Enlisted.UnionWith(other.Enlisted);
     HasChanges = HasChanges || other.HasChanges;
     ListMerge(ref Fx, other.Fx);
     ListMerge(ref SyncFx, other.SyncFx);
 }
Ejemplo n.º 2
0
        /// <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;
            }
        }
Ejemplo n.º 3
0
        public static void Fire(TransItems items)
        {
            var theList = _whenCommitingSubs;

            if (theList == null)
            {
                return;
            }

            var fields = items.GetFields();

            theList.Select(cs => (Action)(() => cs.Act(fields))).SafeRun();
        }
Ejemplo n.º 4
0
        /// <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);
                }
            }
        }