2
0

ConcurrentSkipList.cs 10 KB

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