// // Copyright (c) Microsoft Corporation. All rights reserved. // // The tree implementation comes from Maurice Herlihy's SXM implementation. // // Use the command line to set the number of threads and operations, to // optionally wrap each operation in an atomic or try_all block, and to require // deterministic output for automated testing. namespace Test { using System.Threading; using System.Runtime.CompilerServices; using System; using Microsoft.Contracts; public enum RunKind { Direct, Atomic, TryAll }; public class Config { // Create deterministic output for bvt public static bool DETERMINISTIC = false; // Kind of concurrent control to use public static RunKind KIND; // Number of threads to run public static int THREADS = 1; // Number of successful operations per thread (-1 => loop forever) public static int OP_COUNT = 10000; // Range of key values, 0..KEY_SPACE_MASK public static int KEY_SPACE_MASK = 0xffff; // Workload mix, should sum to 0x100 public static int LOOKUP_FRAC = 0xc0; public static int REMOVE_FRAC = 0x20; public static int INSERT_FRAC = 0x20; } // RBTree benchmark originally from Maurice Herlihy's SXM. public class RBTree { public enum Color {BLACK, RED}; public static int INITIAL_SIZE = Config.KEY_SPACE_MASK / 2; protected RBNode root; // sentinelNode is convenient way of indicating a leaf node. public static RBNode sentinelNode; public int Shadow = 0; public int Actual() { return ActualFrom(root); } public int ActualFrom(RBNode r) { if (r == sentinelNode) { return 0; } else { return r.Value + ActualFrom(r.Left) + ActualFrom(r.Right); } } /// /// Initializes the tree shared by all the threads. /// [NotDelayed] public RBTree() { // set up the sentinel node. the sentinel node is the key to a successful // implementation and for understanding the red-black tree properties. sentinelNode = new RBNode(); sentinelNode.Left = null; sentinelNode.Right = null; sentinelNode.Parent = null; sentinelNode.Color = Color.BLACK; root = sentinelNode; this.root.Value = Int32.MinValue; this.root.Color = Color.BLACK; int size = 0; Object o = new Object(); Random random = new Random(o.GetHashCode()); while (size < INITIAL_SIZE) { int n = random.Next(); // int v = (n >> 8) & 0xfff; int v = (n >> 8) & Config.KEY_SPACE_MASK; if ((bool)Insert(v)) { size++; Shadow += v; } } } /// /// Inserts an element into the integer set, if it is not already present. /// public bool Insert(int key) { // traverse tree - find where node belongs RBNode temp = root; RBNode parent = null; while (temp != sentinelNode) { // find Parent parent = temp; if (key == temp.Value) { return false; } else if (key > temp.Value) { temp = temp.Right; } else { temp = temp.Left; } } // setup node RBNode node = new RBNode(); node.Parent = parent; node.Value = key; node.Left = sentinelNode; node.Right = sentinelNode; // insert node into tree starting at parent's location if (node.Parent != null) { if (node.Value > node.Parent.Value) { node.Parent.Right = node; } else node.Parent.Left = node; } else root = node; // first node added RestoreAfterInsert(node); // restore red-black properties return true; } /// /// Tests whether item is present. /// /// whether the item was present public bool Contains(int key) { RBNode node = root; // begin at root bool result = false; // traverse tree until node is found while (node != sentinelNode) { if (key == node.Value) { result = true; break; } else if (key < node.Value) { node = node.Left; } else { node = node.Right; } } return result; } /// /// Remove item if present. /// /// whether the item was removed public bool Remove(int key) { // find node RBNode node; node = root; while (node != sentinelNode) { if (key == node.Value) { break; } else if (key < node.Value) { node = node.Left; } else { node = node.Right; } } if (node == sentinelNode) return false; // key not found Delete(node); return true; } /// /// Delete a node /// /// node to be deleted private void Delete(RBNode z) { // A node to be deleted will be: // 1. a leaf with no children // 2. have one child // 3. have two children // If the deleted node is red, the red black properties still hold. // If the deleted node is black, the tree needs rebalancing RBNode x; RBNode y; // work node // find the replacement node (the successor to x) - the node one with // at *most* one child. if (z.Left == sentinelNode || z.Right == sentinelNode) y = z; // node has sentinel as a child else { // z has two children, find replacement node which will // be the leftmost node greater than z y = z.Right; // traverse right subtree while (y.Left != sentinelNode) // to find next node in sequence y = y.Left; } // at this point, y contains the replacement node. it's content will be copied // to the values in the node to be deleted // x (y's only child) is the node that will be linked to y's old parent. if (y.Left != sentinelNode) x = y.Left; else x = y.Right; // replace x's parent with y's parent and // link x to proper subtree in parent // this removes y from the chain x.Parent = y.Parent; if (y.Parent != null) if(y == y.Parent.Left) y.Parent.Left = x; else y.Parent.Right = x; // missing enlist for update else root = x; // make x the root node // copy the values from y (the replacement node) to the node being deleted. // note: this effectively deletes the node. if (y != z) { z.Value = y.Value; } if (y.Color == Color.BLACK) RestoreAfterDelete(x); } /// /// RestoreAfterDelete /// Deletions from red-black trees may destroy the red-black /// properties. Examine the tree and restore. Rotations are normally /// required to restore it /// private void RestoreAfterDelete(RBNode x) { // maintain Red-Black tree balance after deleting node RBNode y; while (x != root && x.Color == Color.BLACK) { if (x == x.Parent.Left) // determine sub tree from parent { y = x.Parent.Right; // y is x's sibling if (y.Color == Color.RED) { // x is black, y is red - make both black and rotate y.Color = Color.BLACK; x.Parent.Color = Color.RED; RotateLeft(x.Parent); y = x.Parent.Right; } if(y.Left.Color == Color.BLACK && y.Right.Color == Color.BLACK) { // children are both black y.Color = Color.RED; // change parent to red x = x.Parent; // move up the tree } else { if (y.Right.Color == Color.BLACK) { y.Left.Color = Color.BLACK; y.Color = Color.RED; RotateRight(y); y = x.Parent.Right; } y.Color = x.Parent.Color; x.Parent.Color = Color.BLACK; y.Right.Color = Color.BLACK; RotateLeft(x.Parent); x = root; } } else { // right subtree - same as code above with right and left swapped y = x.Parent.Left; if (y.Color == Color.RED) { y.Color = Color.BLACK; x.Parent.Color = Color.RED; RotateRight (x.Parent); y = x.Parent.Left; } if(y.Right.Color == Color.BLACK && y.Left.Color == Color.BLACK) { y.Color = Color.RED; x = x.Parent; } else { if (y.Left.Color == Color.BLACK) { y.Right.Color = Color.BLACK; y.Color = Color.RED; RotateLeft(y); y = x.Parent.Left; } y.Color = x.Parent.Color; x.Parent.Color = Color.BLACK; y.Left.Color = Color.BLACK; RotateRight(x.Parent); x = root; } } } x.Color = Color.BLACK; } private void RestoreAfterInsert(RBNode x) { RBNode y; // maintain red-black tree properties after adding x while (x != root && x.Parent.Color == Color.RED) { // Parent node is .Colored red; if (x.Parent == x.Parent.Parent.Left) // determine traversal path { // is it on the Left or Right subtree? y = x.Parent.Parent.Right; // get uncle if (y != null && y.Color == Color.RED) { // uncle is red; change x's Parent and uncle to black x.Parent.Color = Color.BLACK; y.Color = Color.BLACK; // grandparent must be red. Why? Every red node that is not // a leaf has only black children x.Parent.Parent.Color = Color.RED; x = x.Parent.Parent; // continue loop with grandparent } else { // uncle is black; determine if x is greater than Parent if (x == x.Parent.Right) { // yes, x is greater than Parent; rotate Left // make x a Left child x = x.Parent; RotateLeft(x); } // no, x is less than Parent x.Parent.Color = Color.BLACK; // make Parent black x.Parent.Parent.Color = Color.RED; // make grandparent black RotateRight(x.Parent.Parent); // rotate right } } else { // x's Parent is on the Right subtree // this code is the same as above with "Left" and "Right" swapped y = x.Parent.Parent.Left; if (y != null && y.Color == Color.RED) { x.Parent.Color = Color.BLACK; y.Color = Color.BLACK; x.Parent.Parent.Color = Color.RED; x = x.Parent.Parent; } else { if (x == x.Parent.Left) { x = x.Parent; RotateRight(x); } x.Parent.Color = Color.BLACK; x.Parent.Parent.Color = Color.RED; RotateLeft(x.Parent.Parent); } } } root.Color = Color.BLACK; // root should always be black } /// /// RotateLeft /// Rebalance the tree by rotating the nodes to the left /// public void RotateLeft(RBNode x) { // pushing node x down and to the Left to balance the tree. x's Right child (y) // replaces x (since y > x), and y's Left child becomes x's Right child // (since it's < y but > x). RBNode y = x.Right; // get x's Right node, this becomes y // set x's Right link x.Right = y.Left; // y's Left child's becomes x's Right child // modify parents if (y.Left != sentinelNode) y.Left.Parent = x; // sets y's Left Parent to x if (y != sentinelNode) y.Parent = x.Parent; // set y's Parent to x's Parent if (x.Parent != null) { // determine which side of it's Parent x was on if (x == x.Parent.Left) x.Parent.Left = y; // set Left Parent to y else x.Parent.Right = y; // set Right Parent to y } else root = y; // at root, set it to y // link x and y y.Left = x; // put x on y's Left if (x != sentinelNode) // set y as x's Parent x.Parent = y; } /// /// RotateRight /// Rebalance the tree by rotating the nodes to the right /// public void RotateRight(RBNode x) { // pushing node x down and to the Right to balance the tree. x's Left child (y) // replaces x (since x < y), and y's Right child becomes x's Left child // (since it's < x but > y). RBNode y = x.Left; // get x's Left node, this becomes y // set x's Right link x.Left = y.Right; // y's Right child becomes x's Left child // modify parents if (y.Right != sentinelNode) y.Right.Parent = x; // sets y's Right Parent to x if (y != sentinelNode) y.Parent = x.Parent; // set y's Parent to x's Parent if (x.Parent != null) // null = root, could also have used root { // determine which side of its Parent x was on if (x == x.Parent.Right) x.Parent.Right = y; // set Right Parent to y else x.Parent.Left = y; // set Left Parent to y } else root = y; // at root, set it to y // link x and y y.Right = x; // put x on y's Right if (x != sentinelNode) // set y as x's Parent x.Parent = y; } private int CountBlackNodes(RBNode root) { if (sentinelNode == root) return 0; int me = (root.Color == Color.BLACK) ? 1 : 0; RBNode left = (sentinelNode == root.Left) ? sentinelNode : root.Left; return me + CountBlackNodes(left); } private int Count(RBNode root) { if (root == sentinelNode) return 0; return 1 + Count(root.Left) + Count(root.Right); } private void RecursiveValidate(RBNode root, int blackNodes, int soFar) { // Empty sub-tree is vacuously OK if (sentinelNode == root) return; Color rootcolor = root.Color; soFar += ((Color.BLACK == rootcolor) ? 1 : 0); root.Marked = true; // Check left side RBNode left = root.Left; if (sentinelNode != left) { if (left.Color == Color.RED && rootcolor == Color.RED) { Console.WriteLine("Two consecutive red nodes!"); return; } if (left.Value >= root.Value) { Console.WriteLine("Tree values out of order!"); return; } if (left.Marked) { Console.WriteLine("Cycle in tree structure!"); return; } RecursiveValidate(left, blackNodes, soFar); } // Check right side RBNode right = root.Right; if (sentinelNode != right) { if (right.Color == Color.RED && rootcolor == Color.RED) { Console.WriteLine("Two consecutive red nodes!"); return; } if (right.Value <= root.Value) { Console.WriteLine("Tree values out of order!"); return; } if (right.Marked) { Console.WriteLine("Cycle in tree structure!"); return; } RecursiveValidate(right, blackNodes, soFar); } // Check black node count if (sentinelNode == root.Left || sentinelNode == root.Right) { if (soFar != blackNodes) { Console.WriteLine("Variable number of black nodes to leaves!"); return; } } // Everything checks out if we get this far. return; } public class RBNode { /// creates new tree node * internal int Value = 0; internal Color Color = Color.RED; internal bool Marked; internal RBNode Parent; internal RBNode Left; internal RBNode Right; } } class STMTest { static Object o = new Object(); public static void ThreadLoop() { Random random = new Random(); int my_shadow = 0; RBTree t = STMTest.t; bool r; int i; try { for (i = 0; ((Config.OP_COUNT == -1) || (i < Config.OP_COUNT)); i ++) { int n = random.Next(); int v = (n >> 8) & Config.KEY_SPACE_MASK; r = false; if ((n & 0xff) < Config.LOOKUP_FRAC) { if (Config.KIND == RunKind.Atomic) { try { r = t.Contains(v); } catch (AtomicFakeException) { } } else if (Config.KIND == RunKind.TryAll) { try { r = t.Contains(v); } catch (TryAllFakeException) { } } else { r = t.Contains(v); } } else if ((n & 0xff) < (Config.LOOKUP_FRAC + Config.REMOVE_FRAC)) { if (Config.KIND == RunKind.Atomic) { try { r = t.Remove(v); } catch (AtomicFakeException) { } } else if (Config.KIND == RunKind.TryAll) { try { r = t.Remove(v); } catch (TryAllFakeException) { } } else { r = t.Remove(v); } if (r) my_shadow -= v; } else { if (Config.KIND == RunKind.Atomic) { try { r = t.Insert(v); } catch (AtomicFakeException) { } } else if (Config.KIND == RunKind.TryAll) { try { r = t.Insert(v); } catch (TryAllFakeException) { } } else { r = t.Insert(v); } if (r) my_shadow += v; } } } catch (Exception e) { lock (o) { Console.WriteLine("Failed with " + e); } throw e; } lock (o) { t.Shadow += my_shadow; } } static RBTree t; private static void Usage(String error) { Console.WriteLine( @"Usage: tree [-t n] [-n n] [-k ] [-d] -t n Use n threads. -n n Perform n operations on each thread. Runs forever if set to -1. -k RunKind Specify if operation kind should be ""atomic"", ""tryall"", or ""direct"". -d Use deterministic output (for automated testing)."); throw new Exception("Illegal input: " + error); } private static String NextArg(string[] args, ref int nextArg) { if (nextArg >= args.Length) { Usage("missing argument"); } return args[nextArg++]; } private static int NextArgInt(string[] args, ref int nextArg, int min) { try { int x = Int32.Parse(NextArg(args, ref nextArg)); if (x < min) { Usage("parameter " + (nextArg-1) + " should be >= " + min); } return x; } catch (FormatException) { Usage("illegal format in parameter " + (nextArg-1)); return 0; } catch (OverflowException) { Usage("overflow in parameter " + (nextArg-1)); return 0; } } public static void Main(string[] args) { if (Config.LOOKUP_FRAC + Config.REMOVE_FRAC + Config.INSERT_FRAC != 0x100) { throw new Exception("Operations fractions should sum to 0x100\n"); } int argIndex = 0; if (argIndex < args.Length) { if ((String.Compare(args[0], "tree", true) == 0) || (String.Compare(args[0], "tree.exe", true) == 0) || (String.Compare(args[0], "tree.x86", true) == 0)) { argIndex++; } } // Read config options while (argIndex < args.Length) { String arg = NextArg(args, ref argIndex); switch (arg) { case "tree": case "tree.exe": case "tree.x86": { // TODO: SINGULARITY: Don't want to see executable name break; } case "-t": { Config.THREADS = NextArgInt(args, ref argIndex, 0); break; } case "-n": { Config.OP_COUNT = NextArgInt(args, ref argIndex, -1); break; } case "-k": { switch (NextArg(args, ref argIndex)) { case "atomic": { Config.KIND = RunKind.Atomic; break; } case "tryall": { Config.KIND = RunKind.TryAll; break; } case "direct": { Config.KIND = RunKind.Direct; break; } default: { Usage("Unexpected kind " + args[2]); break; } } break; } case "-d": { Config.DETERMINISTIC = true; break; } default: { Usage("unknown parameter: " + arg); break; } } } // Initialise the tree t = new RBTree(); // Start the threads // TODO: SINGULARITY: Implement Thread.Name // Thread.CurrentThread.Name = "Main"; Thread[] threads = new Thread[Config.THREADS]; for (int i = 0; i < Config.THREADS; i ++) { threads[i] = new Thread (new ThreadStart (ThreadLoop)); // TODO: SINGULARITY: Implement Thread.Name // threads[i].Name = "swapping-thread-" + i; threads[i].Start(); } // Wait for the threads to complete for (int i = 0; i < Config.THREADS; i ++) { threads[i].Join(); } // Check that dead-reckoned sums agree with what we get // traversing the tree int s = t.Shadow; int a = t.Actual(); if (Config.DETERMINISTIC) { Console.WriteLine("Shadow - Actual = " + (s - a)); } else { Console.WriteLine("Shadow = " + s); Console.WriteLine("Actual = " + a); } if (s != a) { throw new Exception("Mismatch"); } } } }