private async Task add(T value, IAccessContext <CSScope> context) { await TaskCollector.With(async tc => { CSScope pessimisticScope = new CSScope(RWScope.ReadWrite, InfiniteIntervalScope.Create( RWScope.ReadWrite, 0, null), $"pessimistic scope for add-{value}"); await context.InChildWithin(pessimisticScope, async childContext => { Tuple <node, int> lastNodeInfo = await getLastNode(childContext); /* At this point, we don’t require write access * to the nodes from 0 to count - 1 anymore, * but IInfiniteIntervalScope is not granular enough * to represent that. * Read tasks for lower indexes could proceed. */ node newNode = new node { Successor = null, Value = value }; tc.Add(appendToLastNode( newNode, lastNodeInfo, childContext)); }); }); }
private async Task appendToLastNode( node newNode, Tuple <node, int> lastNodeInfo, IAccessContext <CSScope> context) { await context.InChildWithin(async childContext => { if (lastNodeInfo == null) { /* Setting first node */ /* Requires an IInfiniteIntervalScope from 0 to infinity, * because setting the first node determines * could also change all other nodes. */ var scope = new CSScope( RWScope.ReadWrite, InfiniteIntervalScope.Create( RWScope.ReadWrite, 0, 1), $"scope for appendToLastNode {newNode.Value}"); /* TRAP: Using context instead of childContext here * may easily lead to a deadlock. */ childContext.RequiredScope = scope; /* Wait for all required scope before writing. */ await childContext.UntilAvailable(); state.FirstNode = newNode; } else { /* Set the new node */ int newIndex = lastNodeInfo.Item2 + 1; var scope = new CSScope( RWScope.ReadWrite, InfiniteIntervalScope.Create( RWScope.ReadWrite, newIndex, newIndex + 1)); childContext.RequiredScope = scope; await childContext.UntilAvailable(); lastNodeInfo.Item1.Successor = newNode; } state.Count++; }); }