ConcurrentSkipList.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. #if NET_4_0
  2. // ConcurrentSkipList.cs
  3. //
  4. // Copyright (c) 2008 Jérémie "Garuma" Laval
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. //
  24. //
  25. using System;
  26. using System.Threading;
  27. using System.Collections;
  28. using System.Collections.Generic;
  29. namespace System.Collections.Concurrent
  30. {
  31. internal class ConcurrentSkipList<T> : IProducerConsumerCollection<T>
  32. {
  33. // Used for randomSeed
  34. [ThreadStatic]
  35. static Random r;
  36. // Used in FindNodes and thus most others methods
  37. // avoid heavy local array creation at each method call and use
  38. // for thread locallity ThreadStatic attribute
  39. [ThreadStaticAttribute]
  40. static Node[] preds;
  41. [ThreadStaticAttribute]
  42. static Node[] succs;
  43. int count = 0;
  44. class Node
  45. {
  46. public readonly int Key;
  47. public T Value;
  48. public readonly int TopLayer;
  49. public readonly Node[] Nexts;
  50. public volatile bool Marked;
  51. public volatile bool FullyLinked;
  52. public readonly object Lock;
  53. public Node (int key, T value, int heightValue)
  54. {
  55. Key = key;
  56. Value = value;
  57. TopLayer = heightValue;
  58. Nexts = new Node [heightValue + 1];
  59. Lock = new object ();
  60. Marked = FullyLinked = false;
  61. }
  62. }
  63. Node leftSentinel;
  64. Node rightSentinel;
  65. const int MaxHeight = 200;
  66. uint randomSeed;
  67. Func<T, int> GetKey;
  68. public ConcurrentSkipList () : this ((value) => value.GetHashCode ())
  69. {
  70. }
  71. public ConcurrentSkipList (IEqualityComparer<T> comparer)
  72. : this ((value) => comparer.GetHashCode (value))
  73. {
  74. }
  75. public ConcurrentSkipList(Func<T, int> hasher)
  76. {
  77. GetKey = hasher;
  78. Init ();
  79. }
  80. void Init ()
  81. {
  82. if (succs == null)
  83. succs = new Node [MaxHeight];
  84. if (preds == null)
  85. preds = new Node [MaxHeight];
  86. leftSentinel = new Node (int.MinValue, default (T), MaxHeight);
  87. rightSentinel = new Node (int.MaxValue, default (T), MaxHeight);
  88. for (int i = 0; i < MaxHeight; i++) {
  89. leftSentinel.Nexts [i] = rightSentinel;
  90. }
  91. // The or ensures that randomSeed != 0
  92. randomSeed = ((uint)Math.Abs (Next())) | 0x0100;
  93. }
  94. public bool TryAdd (T value)
  95. {
  96. if (value == null)
  97. throw new ArgumentNullException ("value");
  98. CleanArrays ();
  99. int topLayer = GetRandomLevel ();
  100. int v = GetKey (value);
  101. while (true) {
  102. int found = FindNode (v, preds, succs);
  103. if (found != -1) {
  104. // A node with the same key already exists
  105. Node nodeFound = succs [found];
  106. if (!nodeFound.Marked) {
  107. SpinWait sw = new SpinWait ();
  108. while (!nodeFound.FullyLinked) {
  109. sw.SpinOnce ();
  110. }
  111. return false;
  112. }
  113. continue;
  114. }
  115. int highestLocked = -1;
  116. try {
  117. bool valid = LockNodes (topLayer, ref highestLocked,
  118. (layer, pred, succ) => !pred.Marked && !succ.Marked && pred.Nexts [layer] == succ);
  119. if (!valid)
  120. continue;
  121. Node newNode = new Node (v, value, topLayer);
  122. for (int layer = 0; layer <= topLayer; layer++) {
  123. newNode.Nexts [layer] = succs [layer];
  124. preds [layer].Nexts [layer] = newNode;
  125. }
  126. newNode.FullyLinked = true;
  127. } finally {
  128. Unlock (preds, highestLocked);
  129. }
  130. Interlocked.Increment (ref count);
  131. return true;
  132. }
  133. }
  134. bool IProducerConsumerCollection<T>.TryTake (out T value)
  135. {
  136. throw new NotSupportedException ();
  137. }
  138. public T[] ToArray ()
  139. {
  140. int countSnapshot = count;
  141. T[] temp = new T [countSnapshot];
  142. CopyTo(temp, 0);
  143. return temp;
  144. }
  145. public void CopyTo (T[] array, int startIndex)
  146. {
  147. IEnumerator<T> e = GetInternalEnumerator ();
  148. for (int i = startIndex; i < array.Length; i++) {
  149. if (!e.MoveNext ())
  150. return;
  151. array [i] = e.Current;
  152. }
  153. e.Dispose ();
  154. }
  155. void ICollection.CopyTo (Array array, int startIndex)
  156. {
  157. T[] temp = array as T[];
  158. if (temp == null)
  159. return;
  160. CopyTo (temp, startIndex);
  161. }
  162. object ICollection.SyncRoot {
  163. get {
  164. return this;
  165. }
  166. }
  167. bool ICollection.IsSynchronized {
  168. get {
  169. return true;
  170. }
  171. }
  172. public bool Remove (T value)
  173. {
  174. if (value == null)
  175. throw new ArgumentNullException ("value");
  176. CleanArrays();
  177. Node toDelete = null;
  178. bool isMarked = false;
  179. int topLayer = -1;
  180. int v = GetKey (value);
  181. while (true) {
  182. int found = FindNode (v, preds, succs);
  183. if (isMarked || (found != -1 && OkToDelete (succs [found], found))) {
  184. // If not marked then logically delete the node
  185. if (!isMarked) {
  186. toDelete = succs [found];
  187. topLayer = toDelete.TopLayer;
  188. Monitor.Enter (toDelete.Lock);
  189. // Now that we have the lock, check if the node hasn't already been marked
  190. if (toDelete.Marked) {
  191. Monitor.Exit (toDelete.Lock);
  192. return false;
  193. }
  194. toDelete.Marked = true;
  195. isMarked = true;
  196. }
  197. int highestLocked = -1;
  198. try {
  199. bool valid = LockNodes (topLayer, ref highestLocked,
  200. (layer, pred, succ) => !pred.Marked && pred.Nexts [layer] == succ);
  201. if (!valid)
  202. continue;
  203. for (int layer = topLayer; layer >= 0; layer--) {
  204. preds [layer].Nexts [layer] = toDelete.Nexts [layer];
  205. }
  206. Monitor.Exit (toDelete.Lock);
  207. } finally {
  208. Unlock (preds, highestLocked);
  209. }
  210. Interlocked.Decrement (ref count);
  211. return true;
  212. } else {
  213. return false;
  214. }
  215. }
  216. }
  217. public bool Contains (T value)
  218. {
  219. if (value == null)
  220. throw new ArgumentNullException ("value");
  221. return ContainsFromHash (GetKey (value));
  222. }
  223. internal bool ContainsFromHash (int hash)
  224. {
  225. CleanArrays ();
  226. int found = FindNode (hash, preds, succs);
  227. return found != -1 && succs [found].FullyLinked && !succs [found].Marked;
  228. }
  229. internal bool GetFromHash (int hash, out T value)
  230. {
  231. value = default (T);
  232. CleanArrays ();
  233. // We are blindly supposing that the hash is correct
  234. // i.e. I trust myself :-)
  235. int found = FindNode (hash, preds, succs);
  236. if (found == -1)
  237. return false;
  238. try {
  239. Monitor.Enter (succs [found].Lock);
  240. Node node = succs [found];
  241. if (node.FullyLinked && !node.Marked) {
  242. value = node.Value;
  243. return true;
  244. }
  245. } finally {
  246. Monitor.Exit (succs [found].Lock);
  247. }
  248. return false;
  249. }
  250. public int Count {
  251. get {
  252. return count;
  253. }
  254. }
  255. IEnumerator<T> IEnumerable<T>.GetEnumerator ()
  256. {
  257. return GetInternalEnumerator ();
  258. }
  259. IEnumerator IEnumerable.GetEnumerator ()
  260. {
  261. return GetInternalEnumerator ();
  262. }
  263. IEnumerator<T> GetInternalEnumerator ()
  264. {
  265. Node curr = leftSentinel;
  266. while ((curr = curr.Nexts [0]) != rightSentinel) {
  267. // If there is an Add operation ongoing we wait a little
  268. // Possible optimization : use a helping scheme
  269. SpinWait sw = new SpinWait ();
  270. while (!curr.FullyLinked) {
  271. sw.SpinOnce ();
  272. }
  273. yield return curr.Value;
  274. }
  275. }
  276. void Unlock(Node[] preds, int highestLocked)
  277. {
  278. for (int i = 0; i <= highestLocked; i++) {
  279. Monitor.Exit (preds [i].Lock);
  280. }
  281. }
  282. bool LockNodes (int topLayer, ref int highestLocked, Func<int, Node, Node, bool> validityTest)
  283. {
  284. Node pred, succ, prevPred = null;
  285. bool valid = true;
  286. for (int layer = 0; valid && (layer <= topLayer); layer++) {
  287. pred = preds [layer];
  288. succ = succs [layer];
  289. if (pred != prevPred) {
  290. // Possible optimization : limit topLayer to the first refused lock
  291. Monitor.Enter (pred.Lock);
  292. highestLocked = layer;
  293. prevPred = pred;
  294. }
  295. valid = validityTest (layer, pred, succ);
  296. }
  297. return valid;
  298. }
  299. int FindNode (int v, Node[] preds, Node[] succs)
  300. {
  301. // With preds and succs we record the path we use for searching v
  302. if (preds.Length != MaxHeight || succs.Length != MaxHeight)
  303. throw new Exception ("precs or succs don't have the good length");
  304. int found = -1;
  305. Node pred = leftSentinel;
  306. // We start at the higher layer
  307. for (int layer = MaxHeight - 1; layer >= 0; layer--) {
  308. Node curr = pred.Nexts [layer];
  309. // In the current layer we find the best position, then the operation will continue on the
  310. // layer just beneath
  311. while (v > curr.Key) {
  312. pred = curr;
  313. curr = curr.Nexts [layer];
  314. }
  315. if (found == -1 && v == curr.Key)
  316. found = layer;
  317. preds [layer] = pred;
  318. succs [layer] = curr;
  319. }
  320. return found;
  321. }
  322. bool OkToDelete (Node candidate, int found)
  323. {
  324. return candidate.FullyLinked && candidate.TopLayer == found && !candidate.Marked;
  325. }
  326. // Taken from Doug Lea's code released in the public domain
  327. int GetRandomLevel ()
  328. {
  329. uint x = randomSeed;
  330. x ^= x << 13;
  331. x ^= x >> 17;
  332. x ^= x << 5;
  333. randomSeed = x;
  334. if ((x & 0x80000001) != 0) // test highest and lowest bits
  335. return 0;
  336. int level = 1;
  337. while (((x >>= 1) & 1) != 0) ++level;
  338. return level;
  339. }
  340. void CleanArrays ()
  341. {
  342. if (succs == null)
  343. succs = new Node [MaxHeight];
  344. if (preds == null)
  345. preds = new Node [MaxHeight];
  346. // Hopefully these are more optimized than a bare for loop
  347. // (I suppose it uses memset internally)
  348. Array.Clear (preds, 0, preds.Length);
  349. Array.Clear (succs, 0, succs.Length);
  350. }
  351. int Next ()
  352. {
  353. if (r == null)
  354. r = new Random ();
  355. return r.Next ();
  356. }
  357. }
  358. }
  359. #endif