1: using System;
2: using System.Collections;
3: using System.Collections.Generic;
4: using System.Threading;
5:
6: namespace Utilities
7: {
8: /// <summary>
9: /// Provides a thread-safe and lock-efficient dictionary. Key must
10: /// implement the generic IComparable interface, and the value must
11: /// be a reference type.
12: /// Enumerations are thread-safe, but will block changes to the
13: /// dictionary.
14: /// The Keys and Values properties are NOT thread-safe and will throw
15: /// exceptions if the dictionary is modified while enumerating those
16: /// objects.
17: ///
18: /// Matthew Potter
19: /// 2007-08-31
20: /// matthew@synapseware.com
21: ///
22: /// You are free to use this code, as long as you leave my author
23: /// information intact.
24: /// </summary>
25: public class SyncLockDictionary<TKey, UValue> : Dictionary<TKey, UValue>
26: where TKey: IComparable<TKey>
27: where UValue: class
28: {
29: /// <summary>
30: /// Defines the read-lock timeout, in milliseconds.
31: /// </summary>
32: protected const int READLOCK_TIMEOUT = 100;
33:
34: /// <summary>
35: /// Defines the write-lock timeout, in milliseconds.
36: /// </summary>
37: protected const int WRITELOCK_TIMEOUT = 250;
38:
39: /// <summary>
40: /// The locking mechanism that provides the thread-safety
41: /// </summary>
42: private ReaderWriterLock _lock = new ReaderWriterLock ();
43:
44: /// <summary>
45: /// Default Constructor
46: /// </summary>
47: public SyncLockDictionary ()
48: : base ()
49: {}
50:
51: /// <summary>
52: /// Copy Constructor
53: /// </summary>
54: /// <param name="copy"></param>
55: public SyncLockDictionary (Dictionary<TKey, UValue> copy)
56: : base (copy)
57: {}
58:
59: /// <summary>
60: /// Constructor
61: /// </summary>
62: /// <param name="comparer"></param>
63: public SyncLockDictionary (IEqualityComparer<TKey> comparer)
64: : base (comparer)
65: {}
66:
67: /// <summary>
68: /// Constructor
69: /// </summary>
70: /// <param name="capacity"></param>
71: public SyncLockDictionary (int capacity)
72: : base (capacity)
73: {}
74:
75: /// <summary>
76: /// Constructor
77: /// </summary>
78: /// <param name="capacity"></param>
79: /// <param name="comparer"></param>
80: public SyncLockDictionary (int capacity, IEqualityComparer<TKey> comparer)
81: : base (capacity, comparer)
82: {
83: }
84:
85: /// <summary>
86: /// Add a new value to the dictionary.
87: /// </summary>
88: /// <param name="key"></param>
89: /// <param name="value"></param>
90: public new void Add (TKey key, UValue value)
91: {
92: try
93: {
94: _lock.AcquireWriterLock (READLOCK_TIMEOUT);
95: if (base.ContainsKey (key))
96: base [key] = value;
97: else
98: base.Add (key, value);
99: }
100: finally
101: {
102: // release reader lock
103: _lock.ReleaseWriterLock ();
104: }
105: }
106:
107: /// <summary>
108: /// Remove a value from the dictionary.
109: /// </summary>
110: /// <param name="key"></param>
111: public new void Remove (TKey key)
112: {
113: try
114: {
115: // get read lock and return if nothing to remove
116: _lock.AcquireReaderLock (READLOCK_TIMEOUT);
117: if (!base.ContainsKey (key))
118: return;
119:
120: // upgrade to writer lock so we can safely remove the item
121: LockCookie lc = _lock.UpgradeToWriterLock (WRITELOCK_TIMEOUT);
122:
123: // remove the item
124: base.Remove (key);
125:
126: // downgrade writer lock
127: _lock.DowngradeFromWriterLock (ref lc);
128: }
129: finally
130: {
131: // release reader lock
132: _lock.ReleaseReaderLock ();
133: }
134: }
135:
136: /// <summary>
137: /// Returns true if the specified key exists in the collection.
138: /// </summary>
139: /// <param name="key"></param>
140: /// <returns></returns>
141: public new bool ContainsKey (TKey key)
142: {
143: try
144: {
145: _lock.AcquireReaderLock (READLOCK_TIMEOUT);
146:
147: return base.ContainsKey (key);
148: }
149: finally
150: { _lock.ReleaseReaderLock (); }
151: }
152:
153: /// <summary>
154: /// Returns true if the specified value exists in the collection.
155: /// </summary>
156: /// <param name="value"></param>
157: /// <returns></returns>
158: public new bool ContainsValue (UValue value)
159: {
160: try
161: {
162: _lock.AcquireReaderLock (READLOCK_TIMEOUT);
163:
164: return base.ContainsValue (value);
165: }
166: finally
167: { _lock.ReleaseReaderLock (); }
168: }
169:
170: /// <summary>
171: /// Gets the value associated with the specified key. Returns
172: /// false if the specified key does not exist in the dictionary.
173: /// </summary>
174: /// <param name="key"></param>
175: /// <param name="value"></param>
176: /// <returns></returns>
177: public new bool TryGetValue (TKey key, out UValue value)
178: {
179: try
180: {
181: _lock.AcquireReaderLock (READLOCK_TIMEOUT);
182:
183: return base.TryGetValue (key, out value);
184: }
185: finally
186: { _lock.ReleaseReaderLock (); }
187: }
188:
189: /// <summary>
190: /// Returns the number of items currently in the dictionary.
191: /// </summary>
192: public new int Count
193: {
194: get
195: {
196: try
197: {
198: _lock.AcquireReaderLock (READLOCK_TIMEOUT);
199:
200: return base.Count;
201: }
202: finally
203: { _lock.ReleaseReaderLock (); }
204: }
205: }
206:
207: /// <summary>
208: /// Returns a thread-safe enumerator for this dictionary (changes
209: /// to the dictionary will not be reflected during an enumeration).
210: /// </summary>
211: /// <returns></returns>
212: public new SafeEnumerator GetEnumerator ()
213: {
214: try
215: {
216: _lock.AcquireReaderLock (READLOCK_TIMEOUT);
217:
218: ArrayList items = new ArrayList ();
219: Enumerator en = base.GetEnumerator ();
220: while (en.MoveNext ())
221: items.Add (en.Current);
222:
223: return new SafeEnumerator (items);
224: }
225: finally
226: { _lock.ReleaseReaderLock (); }
227: }
228:
229: /// <summary>
230: /// Returns a reference to the Keys collection.
231: /// </summary>
232: public new SafeEnumerator Keys
233: {
234: get
235: {
236: try
237: {
238: _lock.AcquireReaderLock (READLOCK_TIMEOUT);
239:
240: ArrayList items = new ArrayList ();
241: foreach (object key in base.Keys)
242: items.Add (key.ToString ());
243:
244: return new SafeEnumerator (items);
245: }
246: finally
247: { _lock.ReleaseReaderLock (); }
248: }
249: }
250:
251: /// <summary>
252: /// Returns a reference to the Values collection.
253: /// </summary>
254: public new SafeEnumerator Values
255: {
256: get
257: {
258: try
259: {
260: _lock.AcquireReaderLock (READLOCK_TIMEOUT);
261:
262: ArrayList items = new ArrayList ();
263: foreach (object key in base.Values)
264: items.Add (key);
265:
266: return new SafeEnumerator (items);
267: }
268: finally
269: { _lock.ReleaseReaderLock (); }
270: }
271: }
272:
273: /// <summary>
274: /// Thread-safe accessor property.
275: /// </summary>
276: /// <param name="key"></param>
277: /// <returns></returns>
278: public new UValue this [TKey key]
279: {
280: get
281: {
282: try
283: {
284: _lock.AcquireReaderLock (READLOCK_TIMEOUT);
285:
286: UValue value = null;
287: if (base.TryGetValue (key, out value))
288: return value;
289:
290: return null;
291: }
292: finally
293: { _lock.ReleaseReaderLock (); }
294: }
295: set
296: {
297: try
298: {
299: _lock.AcquireWriterLock (WRITELOCK_TIMEOUT);
300: try
301: {
302: if (base.ContainsKey (key))
303: base [key] = value;
304: else
305: base.Add (key, value);
306: }
307: finally
308: { _lock.ReleaseWriterLock (); }
309: }
310: finally
311: {}
312: }
313: }
314:
315: /// <summary>
316: /// Gets an object that can be used to synchronize
317: /// access to the dictionary.
318: /// </summary>
319: public object SyncRoot
320: {
321: get
322: { return this; }
323: }
324: }
325:
326: /// <summary>
327: /// Provides a thread-safe enumerator for a SyncLockDictionary object.
328: /// </summary>
329: public class SafeEnumerator : System.Collections.IEnumerator, IEnumerable
330: {
331: private IList _items;
332: private int _current;
333:
334:
335: /// <summary>
336: ///
337: /// </summary>
338: /// <param name="synclock"></param>
339: public SafeEnumerator (IList items)
340: {
341: _items = items;
342: _current = -1;
343: }
344:
345: /// <summary>
346: ///
347: /// </summary>
348: public object Current
349: {
350: get
351: {
352: if (_current < 0 || 0 == _items.Count)
353: return null;
354:
355: return _items [_current];
356: }
357: }
358:
359: /// <summary>
360: ///
361: /// </summary>
362: public void Dispose ()
363: {}
364:
365: /// <summary>
366: ///
367: /// </summary>
368: object IEnumerator.Current
369: {
370: get
371: {
372: return this.Current;
373: }
374: }
375:
376: /// <summary>
377: ///
378: /// </summary>
379: /// <returns></returns>
380: public bool MoveNext ()
381: {
382: if (_current >= _items.Count)
383: return false;
384:
385: _current++;
386: if (_current < _items.Count)
387: return true;
388:
389: return false;
390: }
391:
392: /// <summary>
393: ///
394: /// </summary>
395: public void Reset ()
396: {
397: _current = -1;
398: }
399:
400: /// <summary>
401: ///
402: /// </summary>
403: /// <returns></returns>
404: IEnumerator IEnumerable.GetEnumerator ()
405: {
406: return _items.GetEnumerator ();
407: }
408: }
409: }