Example #1
0
        internal void MergeReadStructsIntoPendingReads(IDictionary <int, List <AbstractStruct> > clientStructsRefs)
        {
            var pendingClientStructRefs = _pendingClientStructRefs;

            foreach (var kvp in clientStructsRefs)
            {
                var client     = kvp.Key;
                var structRefs = kvp.Value;

                if (!pendingClientStructRefs.TryGetValue(client, out var pendingStructRefs))
                {
                    pendingClientStructRefs[client] = new PendingClientStructRef {
                        Refs = structRefs
                    };
                }
                else
                {
                    // Merge into existing structRefs.
                    if (pendingStructRefs.NextReadOperation > 0)
                    {
                        pendingStructRefs.Refs.RemoveRange(0, pendingStructRefs.NextReadOperation);
                    }

                    var merged = pendingStructRefs.Refs;
                    for (int i = 0; i < structRefs.Count; i++)
                    {
                        merged.Add(structRefs[i]);
                    }
                    merged.Sort((a, b) => a.Id.Clock - b.Id.Clock);

                    pendingStructRefs.NextReadOperation = 0;
                    pendingStructRefs.Refs = merged;
                }
            }
        }
Example #2
0
        /// <summary>
        /// Resume computing structs generated by struct readers.
        /// <br/>
        /// While there is something to do, we integrate structs in this order:
        /// 1. Top element on stack, if stack is not empty.
        /// 2. Next element from current struct reader (if empty, use next struct reader).
        /// <br/>
        /// If struct causally depends on another struct (ref.missing), we put next reader of
        /// 'ref.id.client' on top of stack.
        /// <br/>
        /// At some point we find a struct that has no causal dependencies, then we start
        /// emptying the stack.
        /// <br/>
        /// It is not possible to have circles: i.e. struct1 (from client1) depends on struct2 (from client2)
        /// depends on struct3 (from client1). Therefore, the max stack size is equal to 'structReaders.length'.
        /// <br/>
        /// This method is implemented in a way so that we can resume computation if this update causally
        /// depends on another update.
        /// </summary>
        internal void ResumeStructIntegration(Transaction transaction)
        {
            // @todo: Don't forget to append stackhead at the end.
            var stack             = _pendingStack;
            var clientsStructRefs = _pendingClientStructRefs;

            if (clientsStructRefs.Count == 0)
            {
                return;
            }

            // Sort them so taht we take the higher id first, in case of conflicts the lower id will probably not conflict with the id from the higher user.
            var clientsStructRefsIds = clientsStructRefs.Keys.ToList();

            clientsStructRefsIds.Sort();

            PendingClientStructRef getNextStructTarget()
            {
                var nextStructsTarget = clientsStructRefs[clientsStructRefsIds[clientsStructRefsIds.Count - 1]];

                while (nextStructsTarget.Refs.Count == nextStructsTarget.NextReadOperation)
                {
                    clientsStructRefsIds.RemoveAt(clientsStructRefsIds.Count - 1);
                    if (clientsStructRefsIds.Count > 0)
                    {
                        nextStructsTarget = clientsStructRefs[clientsStructRefsIds[clientsStructRefsIds.Count - 1]];
                    }
                    else
                    {
                        _pendingClientStructRefs.Clear();
                        return(null);
                    }
                }

                return(nextStructsTarget);
            }

            var curStructsTarget = getNextStructTarget();

            if (curStructsTarget == null && stack.Count == 0)
            {
                return;
            }

            var stackHead = stack.Count > 0 ? stack.Pop() : curStructsTarget.Refs[curStructsTarget.NextReadOperation++];
            // Caching the state because it is used very often.
            var state = new Dictionary <int, int>();

            // Iterate over all struct readers until we are done.
            while (true)
            {
                if (!state.TryGetValue(stackHead.Id.Client, out int localClock))
                {
                    localClock = GetState(stackHead.Id.Client);
                    state[stackHead.Id.Client] = localClock;
                }

                var offset = stackHead.Id.Clock < localClock ? localClock - stackHead.Id.Clock : 0;
                if (stackHead.Id.Clock + offset != localClock)
                {
                    // A previous message from this client is missing.
                    // Check if there is a pending structRef with a smaller clock and switch them.
                    if (!clientsStructRefs.TryGetValue(stackHead.Id.Client, out var structRefs))
                    {
                        structRefs = new PendingClientStructRef();
                    }

                    if (structRefs.Refs.Count != structRefs.NextReadOperation)
                    {
                        var r = structRefs.Refs[structRefs.NextReadOperation];
                        if (r.Id.Clock < stackHead.Id.Clock)
                        {
                            // Put ref with smaller clock on stack instead and continue.
                            structRefs.Refs[structRefs.NextReadOperation] = stackHead;
                            stackHead = r;

                            // Sort the set because this approach might bring the list out of order.
                            structRefs.Refs.RemoveRange(0, structRefs.NextReadOperation);
                            structRefs.Refs.Sort((a, b) => a.Id.Clock - b.Id.Clock);

                            structRefs.NextReadOperation = 0;
                            continue;
                        }
                    }

                    // Wait until missing struct is available.
                    stack.Push(stackHead);
                    return;
                }

                var missing = stackHead.GetMissing(transaction, this);
                if (missing == null)
                {
                    if (offset == 0 || offset < stackHead.Length)
                    {
                        stackHead.Integrate(transaction, offset);
                        state[stackHead.Id.Client] = stackHead.Id.Clock + stackHead.Length;
                    }

                    // Iterate to next stackHead.
                    if (stack.Count > 0)
                    {
                        stackHead = stack.Pop();
                    }
                    else if (curStructsTarget != null && curStructsTarget.NextReadOperation < curStructsTarget.Refs.Count)
                    {
                        stackHead = curStructsTarget.Refs[curStructsTarget.NextReadOperation++];
                    }
                    else
                    {
                        curStructsTarget = getNextStructTarget();
                        if (curStructsTarget == null)
                        {
                            // We are done!
                            break;
                        }
                        else
                        {
                            stackHead = curStructsTarget.Refs[curStructsTarget.NextReadOperation++];
                        }
                    }
                }
                else
                {
                    // Get the struct reader that has the missing struct.
                    if (!clientsStructRefs.TryGetValue(missing.Value, out var structRefs))
                    {
                        structRefs = new PendingClientStructRef();
                    }

                    if (structRefs.Refs.Count == structRefs.NextReadOperation)
                    {
                        // This update message causally depends on another update message.
                        stack.Push(stackHead);
                        return;
                    }

                    stack.Push(stackHead);
                    stackHead = structRefs.Refs[structRefs.NextReadOperation++];
                }
            }

            _pendingClientStructRefs.Clear();
        }