* Fix System.Collections.Concurrent API mismatches
authorJérémie Laval <jeremie.laval@gmail.com>
Fri, 31 Jul 2009 12:36:18 +0000 (12:36 -0000)
committerJérémie Laval <jeremie.laval@gmail.com>
Fri, 31 Jul 2009 12:36:18 +0000 (12:36 -0000)
* Update unit tests with the modified API

svn path=/trunk/mcs/; revision=139172

12 files changed:
mcs/class/corlib/ChangeLog
mcs/class/corlib/System.Collections.Concurrent/ChangeLog
mcs/class/corlib/System.Collections.Concurrent/ConcurrentDictionary.cs
mcs/class/corlib/System.Collections.Concurrent/ConcurrentQueue.cs
mcs/class/corlib/System.Collections.Concurrent/ConcurrentSkipList.cs
mcs/class/corlib/System.Collections.Concurrent/ConcurrentStack.cs
mcs/class/corlib/System.Collections.Concurrent/Partitionners/EnumerablePartitioner.cs
mcs/class/corlib/System.Collections.Concurrent/Partitionners/ListPartitioner.cs
mcs/class/corlib/Test/System.Collections.Concurrent/ChangeLog
mcs/class/corlib/Test/System.Collections.Concurrent/ConcurrentDictionaryTests.cs
mcs/class/corlib/Test/System.Collections.Concurrent/ConcurrentQueueTests.cs
mcs/class/corlib/corlib_test.dll.sources

index c8a006b1044acf4a263312c471ddd3c222bb189c..cdf0e06c53c5f494498c806b30f860a022c3a93d 100644 (file)
@@ -1,3 +1,8 @@
+2009-07-31 Jérémie Laval  <jeremie.laval@gmail.com>
+
+       * corlib_test.dll.source: Add ConcurrentSkipList class
+       for internal testing
+
 2009-07-30 Jérémie Laval  <jeremie.laval@gmail.com>
 
        * corlib.dll.source: Add ParallelFx files.
index 270f4f11c0417002a1ba8dea1f518d3626393271..1179bbb50787ec5ce583b4a4a9a9d8de8a1df0a5 100644 (file)
@@ -1,3 +1,9 @@
+2009-07-31 Jérémie Laval  <jeremie.laval@gmail.com>
+
+       * ConcurrentDictionary.cs:
+       * ConcurrentQueue.cs:
+       * ConcurrentStack.cs: Make the classes comply with B1 API
+
 2009-07-27  Jérémie Laval  <jeremie.laval@gmail.com>
 
        * BlockingCollection.cs:
index f10e5ce07819eb58809fbb06977d85b4406cad79..695869fb5c1977ee75b14eb173a983cea34e5d6d 100644 (file)
@@ -27,13 +27,13 @@ using System;
 using System.Threading;
 using System.Collections;
 using System.Collections.Generic;
+using System.Runtime.Serialization;
 
 namespace System.Collections.Concurrent
 {
-       public class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>
-               , ICollection<KeyValuePair<TKey, TValue>>, ICollection, IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable
-                       , IProducerConsumerCollection<KeyValuePair<TKey, TValue>>
-               
+       public class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>, 
+         ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, 
+         IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback
        {
                class Pair
                {
@@ -64,19 +64,71 @@ namespace System.Collections.Concurrent
                
                // Assumption: a List<T> is never empty
                ConcurrentSkipList<Basket> container
-                       = new ConcurrentSkipList<Basket> ((value) => value [0].GetHashCode ());
+                       = new ConcurrentSkipList<Basket> ((value) => value[0].GetHashCode ());
                int count;
                int stamp = int.MinValue;
+               IEqualityComparer<TKey> comparer;
+               
+               public ConcurrentDictionary () : this (EqualityComparer<TKey>.Default)
+               {
+               }
+               
+               public ConcurrentDictionary (IEnumerable<KeyValuePair<TKey, TValue>> values)
+                       : this (values, EqualityComparer<TKey>.Default)
+               {
+                       foreach (KeyValuePair<TKey, TValue> pair in values)
+                               Add (pair.Key, pair.Value);
+               }
+               
+               public ConcurrentDictionary (IEqualityComparer<TKey> comparer)
+               {
+                       this.comparer = comparer;
+               }
+               
+               public ConcurrentDictionary (IEnumerable<KeyValuePair<TKey, TValue>> values, IEqualityComparer<TKey> comparer)
+                       : this (comparer)
+               {                       
+                       foreach (KeyValuePair<TKey, TValue> pair in values)
+                               Add (pair.Key, pair.Value);
+               }
+               
+               // Parameters unused
+               public ConcurrentDictionary (int concurrencyLevel, int capacity)
+                       : this (EqualityComparer<TKey>.Default)
+               {
+                       
+               }
+               
+               public ConcurrentDictionary (int concurrencyLevel, 
+                                            IEnumerable<KeyValuePair<TKey, TValue>> values,
+                                            IEqualityComparer<TKey> comparer)
+                       : this (values, comparer)
+               {
+                       
+               }
+               
+               // Parameters unused
+               public ConcurrentDictionary (int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer)
+                       : this (comparer)
+               {
+                       
+               }
                
-               public ConcurrentDictionary ()
+               internal ConcurrentDictionary (SerializationInfo info, StreamingContext context)
                {
+                       throw new NotImplementedException ();
                }
                
-               public void Add (TKey key, TValue value)
+               void Add (TKey key, TValue value)
                {
                        while (!TryAdd (key, value));
                }
                
+               void IDictionary<TKey, TValue>.Add (TKey key, TValue value)
+               {
+                       Add (key, value);
+               }
+               
                public bool TryAdd (TKey key, TValue value)
                {
                        Interlocked.Increment (ref count);
@@ -88,7 +140,7 @@ namespace System.Collections.Concurrent
                                // Find a maybe more sexy locking scheme later
                                lock (basket) {
                                        foreach (var p in basket) {
-                                               if (p.Key.Equals (key))
+                                               if (comparer.Equals (p.Key, key))
                                                        throw new ArgumentException ("An element with the same key already exists");
                                        }
                                        basket.Add (new Pair (key, value));
@@ -108,7 +160,7 @@ namespace System.Collections.Concurrent
                        Add (pair.Key, pair.Value);
                }
                
-               public TValue GetValue (TKey key)
+               TValue GetValue (TKey key)
                {
                        TValue temp;
                        if (!TryGetValue (key, out temp))
@@ -126,7 +178,7 @@ namespace System.Collections.Concurrent
                                return false;
                        
                        lock (basket) {
-                               Pair pair = basket.Find ((p) => p.Key.Equals (key));
+                               Pair pair = basket.Find ((p) => comparer.Equals (p.Key, key));
                                if (pair == null)
                                        return false;
                                value = pair.Value;
@@ -135,6 +187,24 @@ namespace System.Collections.Concurrent
                        return true;
                }
                
+               public bool TryUpdate (TKey key, TValue newValue, TValue comparand)
+               {
+                       Basket basket;
+                       if (!TryGetBasket (key, out basket))
+                               return false;
+                       
+                       lock (basket) {
+                               Pair pair = basket.Find ((p) => comparer.Equals (p.Key, key));
+                               if (pair.Value.Equals (comparand)) {
+                                       pair.Value = newValue;
+                                       
+                                       return true;
+                               }
+                       }
+                       
+                       return false;
+               }
+               
                public TValue this[TKey key] {
                        get {
                                return GetValue (key);
@@ -146,7 +216,7 @@ namespace System.Collections.Concurrent
                                        return;
                                }
                                lock (basket) {
-                                       Pair pair = basket.Find ((p) => p.Key.Equals (key));
+                                       Pair pair = basket.Find ((p) => comparer.Equals (p.Key, key));
                                        if (pair == null)
                                                throw new InvalidOperationException ("pair is null, shouldn't be");
                                        pair.Value = value;
@@ -155,15 +225,24 @@ namespace System.Collections.Concurrent
                        }
                }
                
-               public bool Remove(TKey key)
+               public bool TryRemove(TKey key, out TValue value)
                {
+                       value = default (TValue);
                        Basket b;
+                       
                        if (!TryGetBasket (key, out b))
                                return false;
                        
                        lock (b) {
+                               TValue temp = default (TValue);
                                // Should always be == 1 but who know
-                               bool result = b.RemoveAll ((p) => p.Key.Equals (key)) >= 1;
+                               bool result = b.RemoveAll ((p) => {
+                                       bool r = comparer.Equals (p.Key, key);
+                                       if (r) temp = p.Value;
+                                       return r;
+                               }) >= 1;
+                               value = temp;
+                               
                                if (result)
                                        Interlocked.Decrement (ref count);
                                
@@ -171,6 +250,18 @@ namespace System.Collections.Concurrent
                        }
                }
                
+               bool Remove (TKey key)
+               {
+                       TValue dummy;
+                       
+                       return TryRemove (key, out dummy);
+               }
+               
+               bool IDictionary<TKey, TValue>.Remove (TKey key)
+               {
+                       return Remove (key);
+               }
+               
                bool ICollection<KeyValuePair<TKey,TValue>>.Remove (KeyValuePair<TKey,TValue> pair)
                {
                        return Remove (pair.Key);
@@ -181,22 +272,49 @@ namespace System.Collections.Concurrent
                        return container.ContainsFromHash (key.GetHashCode ());
                }
                
-               bool ICollection<KeyValuePair<TKey,TValue>>.Contains (KeyValuePair<TKey, TValue> pair)
+               bool IDictionary.Contains (object key)
                {
-                       return ContainsKey (pair.Key);
+                       if (!(key is TKey))
+                               return false;
+                       
+                       return ContainsKey ((TKey)key);
                }
                
-               public bool TryAdd (KeyValuePair<TKey, TValue> item)
+               void IDictionary.Remove (object key)
                {
-                       Add (item.Key, item.Value);
+                       if (!(key is TKey))
+                               return;
                        
-                       return true;
+                       Remove ((TKey)key);
                }
                
-               public bool TryTake (out KeyValuePair<TKey, TValue> item)
+               object IDictionary.this [object key]
                {
-                       item = default (KeyValuePair<TKey, TValue>);
-                       return false;
+                       get {
+                               if (!(key is TKey))
+                                       throw new ArgumentException ("key isn't of correct type", "key");
+                               
+                               return this[(TKey)key];
+                       }
+                       set {
+                               if (!(key is TKey) || !(value is TValue))
+                                       throw new ArgumentException ("key or value aren't of correct type");
+                               
+                               this[(TKey)key] = (TValue)value;
+                       }
+               }
+               
+               void IDictionary.Add (object key, object value)
+               {
+                       if (!(key is TKey) || !(value is TValue))
+                               throw new ArgumentException ("key or value aren't of correct type");
+                       
+                       Add ((TKey)key, (TValue)value);
+               }
+               
+               bool ICollection<KeyValuePair<TKey,TValue>>.Contains (KeyValuePair<TKey, TValue> pair)
+               {
+                       return ContainsKey (pair.Key);
                }
                
                public KeyValuePair<TKey,TValue>[] ToArray ()
@@ -219,7 +337,19 @@ namespace System.Collections.Concurrent
                        }
                }
                
-               public bool IsReadOnly {
+               public bool IsEmpty {
+                       get {
+                               return count == 0;
+                       }
+               }
+               
+               bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
+                       get {
+                               return false;
+                       }
+               }
+               
+               bool IDictionary.IsReadOnly {
                        get {
                                return false;
                        }
@@ -256,11 +386,16 @@ namespace System.Collections.Concurrent
                        CopyTo (arr, startIndex, count);
                }
                
-               public void CopyTo (KeyValuePair<TKey, TValue>[] array, int startIndex)
+               void CopyTo (KeyValuePair<TKey, TValue>[] array, int startIndex)
                {
                        CopyTo (array, startIndex, count);
                }
                
+               void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue>[] array, int startIndex)
+               {
+                       CopyTo (array, startIndex);
+               }
+               
                void CopyTo (KeyValuePair<TKey, TValue>[] array, int startIndex, int num)
                {
                        // TODO: This is quite unsafe as the count value will likely change during
@@ -307,17 +442,26 @@ namespace System.Collections.Concurrent
                        }
                }
 
+               void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
+               {
+                       throw new NotImplementedException ();
+               }
+               
                bool ICollection.IsSynchronized {
-                       get {
-                               return true;
-                       }
+                       get { return true; }
+               }
+
+               void IDeserializationCallback.OnDeserialization (object sender)
+               {
+                       throw new NotImplementedException ();
                }
                
-               public bool IsFixedSize {
+               bool IDictionary.IsFixedSize {
                        get {
                                return false;
                        }
                }
+                       
                
                bool TryGetBasket (TKey key, out Basket basket)
                {
index bcee21bb2128da49c008001948f4c40eb5bf8c95..5ff8800e679e6f47a6bd6a31b883ccda6b9ba967 100644 (file)
@@ -142,7 +142,7 @@ namespace System.Collections.Concurrent
                        return true;
                }
                
-               public void Clear ()
+               internal void Clear ()
                {
                        count = 0;
                        tail = head = new Node ();
@@ -171,7 +171,7 @@ namespace System.Collections.Concurrent
                        }
                }
                
-               public void CopyTo (Array array, int index)
+               void ICollection.CopyTo (Array array, int index)
                {
                        T[] dest = array as T[];
                        if (dest == null)
@@ -195,7 +195,7 @@ namespace System.Collections.Concurrent
                        return dest;
                }
                
-               public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
+               void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
                {
                        throw new NotImplementedException ();
                }
@@ -204,7 +204,7 @@ namespace System.Collections.Concurrent
                        get { return true; }
                }
 
-               public virtual void OnDeserialization (object sender)
+               void IDeserializationCallback.OnDeserialization (object sender)
                {
                        throw new NotImplementedException ();
                }
index a3fd5fea289e2e5eaf0f416572541744ffb83c4f..e433ed5e6731c8dc556c8313faa7134e1a42af43 100644 (file)
@@ -31,7 +31,7 @@ using System.Collections.Generic;
 namespace System.Collections.Concurrent
 {
        
-       public class ConcurrentSkipList<T> : IProducerConsumerCollection<T>
+       internal class ConcurrentSkipList<T> : IProducerConsumerCollection<T>
        {
                // Used for randomSeed
                [ThreadStatic]
index bdae50af00d2c83f45233ec8a3f6bbd06089dc18..523abc0f025f580832597cbd8ff11500a5783d90 100644 (file)
@@ -62,24 +62,45 @@ namespace System.Collections.Concurrent
                        return true;
                }
                
-               /// <summary>
-               /// </summary>
-               /// <param name="element"></param>
                public void Push (T element)
                {
                        Node temp = new Node ();
                        temp.Value = element;
-                       
                        do {
-                               temp.Next = head;
-                       } while (Interlocked.CompareExchange<Node> (ref head, temp, temp.Next) != temp.Next);
+                         temp.Next = head;
+                       } while (Interlocked.CompareExchange (ref head, temp, temp.Next) != temp.Next);
                        
                        Interlocked.Increment (ref count);
                }
+
+               public void PushRange (T[] values)
+               {
+                 PushRange (values, 0, values.Length);
+               }
+
+               public void PushRange (T[] values, int start, int length)
+               {
+                 Node insert = null;
+                 Node first = null;
+
+                 for (int i = start; i < length; i++) {
+                       Node temp = new Node ();
+                       temp.Value = values[i];
+                       temp.Next = insert;
+                       insert = temp;
+
+                       if (first == null)
+                         first = temp;
+                 }
+                 
+                 do {
+                       first.Next = head;
+                 } while (Interlocked.CompareExchange (ref head, insert, first.Next) != first.Next);
+
+                 Interlocked.Add (ref count, length);
+               }
+
                
-               /// <summary>
-               /// </summary>
-               /// <returns></returns>
                public bool TryPop (out T value)
                {
                        Node temp;
@@ -90,17 +111,46 @@ namespace System.Collections.Concurrent
                                        value = default (T);
                                        return false;
                                }
-                       } while (Interlocked.CompareExchange<Node> (ref head, temp.Next, temp) != temp);
+                       } while (Interlocked.CompareExchange (ref head, temp.Next, temp) != temp);
                        
                        Interlocked.Decrement (ref count);
                        
                        value = temp.Value;
                        return true;
                }
+
+               public int TryPopRange (T[] values)
+               {
+                 return TryPopRange (values, 0, values.Length);
+               }
+
+               public int TryPopRange (T[] values, int startIndex, int count)
+               {
+                 int insertIndex = startIndex;
+                 Node temp;
+                 Node end;
+
+                 do {
+                       temp = head;
+                       if (temp == null)
+                         return -1;
+                       end = temp;
+                       for (int j = 0; j < count - 1; j++) {
+                         end = end.Next;
+                         if (end == null)
+                               break;
+                       }
+                 } while (Interlocked.CompareExchange (ref head, end, temp) != temp);
+                 
+                 int i;
+                 for (i = startIndex; i < count && temp != null; i++) {
+                       values[i] = temp.Value;
+                       temp = temp.Next;
+                 }
+
+                 return i - 1;
+               }
                
-               /// <summary>
-               /// </summary>
-               /// <returns></returns>
                public bool TryPeek (out T value)
                {
                        Node myHead = head;
@@ -146,7 +196,7 @@ namespace System.Collections.Concurrent
                        }
                }
                
-               public void CopyTo (Array array, int index)
+               void ICollection.CopyTo (Array array, int index)
                {
                        T[] dest = array as T[];
                        if (dest == null)
@@ -163,7 +213,7 @@ namespace System.Collections.Concurrent
                        }
                }
                
-               public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
+               void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
                {
                        throw new NotImplementedException ();
                }
@@ -172,7 +222,7 @@ namespace System.Collections.Concurrent
                        get { return true; }
                }
 
-               public virtual void OnDeserialization (object sender)
+               void IDeserializationCallback.OnDeserialization (object sender)
                {
                        throw new NotImplementedException ();
                }
index 351c4bfeb5aaede92b407b8194287b708ca3416f..da1166776ac96bb09a9bbf2344c3361c0d560974 100644 (file)
@@ -31,7 +31,7 @@ using System.Collections.Generic;
 
 namespace System.Collections.Concurrent
 {
-       public class EnumerablePartitioner<T> : OrderablePartitioner<T>
+       internal class EnumerablePartitioner<T> : OrderablePartitioner<T>
        {
                IEnumerable<T> source;
 #if USE_MONITOR
index 1cd1a56ccc8524e44e8841e67d0c1bb66822f250..527df2d23d9c176c7dcff479d419f39eaacf61bf 100644 (file)
@@ -30,7 +30,7 @@ using System.Collections.Generic;
 
 namespace System.Collections.Concurrent
 {
-       public class ListPartitioner<T> : OrderablePartitioner<T>
+       internal class ListPartitioner<T> : OrderablePartitioner<T>
        {
                IList<T> source;
                
index dd8b17b67f01e1c6cd9fb64d87608209514f9558..d1a71fde2fd80590ffe6ee48e344597744fea7fe 100644 (file)
@@ -1,3 +1,8 @@
+2009-07-31 Jérémie Laval  <jeremie.laval@gmail.com>
+
+       * ConcurrentDictionary.cs:
+       * ConcurrentQueue.cs: Adapt test to new API
+
 2009-07-27  Jérémie Laval  <jeremie.laval@gmail.com>
 
        * BlockingCollectionTests.cs:
index db4e086f1e95ac88acfc26be28d7faf1a44cabdb..937c508013af7da9d164a620a230bec1545805f5 100644 (file)
@@ -38,25 +38,27 @@ namespace ParallelFxTests
                ConcurrentDictionary<string, int> map;
                
                [SetUp]
-               public void Setup()
+               public void Setup ()
                {
-                       map = new ConcurrentDictionary<string, int>();
+                       map = new ConcurrentDictionary<string, int> ();
                        AddStuff();
                }
                
-               void AddStuff()
+               void AddStuff ()
                {
-                       map.Add("foo", 1);
-                       map.Add("bar", 2);
-                       map.Add("foobar", 3);
+                 map.TryAdd ("foo", 1);
+                       map.TryAdd ("bar", 2);
+                       map.TryAdd ("foobar", 3);
                }
                
                [Test]
-               public void AddWithoutDuplicateTest()
+               public void AddWithoutDuplicateTest ()
                {
-                       map.Add("baz", 2);
+                       map.TryAdd("baz", 2);
+                       int val;
                        
-                       Assert.AreEqual(2, map.GetValue("baz"));
+                       Assert.IsTrue (map.TryGetValue("baz", out val));
+                       Assert.AreEqual(2, val);
                        Assert.AreEqual(2, map["baz"]);
                        Assert.AreEqual(4, map.Count);
                }
@@ -98,18 +100,19 @@ namespace ParallelFxTests
                                Setup ();
                                int index = 0;
                                bool r1 = false, r2 = false, r3 = false;
+                               int val;
                                
                                ParallelTestHelper.ParallelStressTest (map, delegate {
                                        int own = Interlocked.Increment (ref index);
                                        switch (own) {
                                        case 1:
-                                               r1 = map.Remove ("foo");
+                                               r1 = map.TryRemove ("foo", out val);
                                                break;
                                        case 2:
-                                               r2 =map.Remove ("bar");
+                                         r2 =map.TryRemove ("bar", out val);
                                                break;
                                        case 3:
-                                               r3 = map.Remove ("foobar");
+                                         r3 = map.TryRemove ("foobar", out val);
                                                break;
                                        }
                                }, 3);
@@ -130,13 +133,13 @@ namespace ParallelFxTests
                [Test, ExpectedException(typeof(ArgumentException))]
                public void AddWithDuplicate()
                {
-                       map.Add("foo", 6);
+                       map.TryAdd("foo", 6);
                }
                
                [Test]
                public void GetValueTest()
                {
-                       Assert.AreEqual(1, map.GetValue("foo"), "#1");
+                 Assert.AreEqual(1, map["foo"], "#1");
                        Assert.AreEqual(2, map["bar"], "#2");
                        Assert.AreEqual(3, map.Count, "#3");
                }
@@ -146,7 +149,7 @@ namespace ParallelFxTests
                {
                        int val;
                        Assert.IsFalse(map.TryGetValue("barfoo", out val));
-                       map.GetValue("barfoo");
+                       val = map["barfoo"];
                }
                
                [Test]
@@ -156,7 +159,6 @@ namespace ParallelFxTests
                        int val;
                        
                        Assert.AreEqual(9, map["foo"], "#1");
-                       Assert.AreEqual(9, map.GetValue("foo"), "#2");
                        Assert.IsTrue(map.TryGetValue("foo", out val), "#3");
                        Assert.AreEqual(9, val, "#4");
                }
index b0990e8c229c3c1a4158965d8bf975156846a549..5b2b909755a762d6bcc3b043f62e03526a728933 100644 (file)
@@ -124,9 +124,6 @@ namespace ParallelFxTests
                        queue.TryDequeue(out value);
                        queue.TryDequeue(out value);
                        Assert.AreEqual(8, queue.Count, "#2");
-                       queue.Clear();
-                       Assert.AreEqual(0, queue.Count, "#3");
-                       Assert.IsTrue(queue.IsEmpty, "#4");
                }
                
                //[Ignore]
@@ -171,7 +168,7 @@ namespace ParallelFxTests
                public void TryDequeueEmptyTestCase()
                {
                        int value;
-                       queue.Clear();
+                       queue = new ConcurrentQueue<int> ();
                        queue.Enqueue(1);
                        Assert.IsTrue(queue.TryDequeue(out value), "#1");
                        Assert.IsFalse(queue.TryDequeue(out value), "#2");
index c4b8591dd2e0d7182bff0a21147994a7caf340a6..27386bf2caf09ebd73dd54efa81731a88de572cc 100644 (file)
@@ -419,6 +419,7 @@ System.Threading.Tasks/TaskTest.cs
 System.Threading.Tasks/FutureTests.cs
 System.Collections.Concurrent/ParallelConcurrentStackTests.cs
 System.Collections.Concurrent/ConcurrentSkipListTests.cs
+../System.Collections.Concurrent/ConcurrentSkipList.cs
 System.Collections.Concurrent/ConcurrentQueueTests.cs
 System.Collections.Concurrent/ConcurrentBagTests.cs
 System.Collections.Concurrent/ConcurrentStackTests.cs