3using System.Diagnostics;
5using System.Threading.Tasks;
16 private static readonly Stopwatch watch =
new Stopwatch();
23 private readonly
bool recordStackTraces;
24 private readonly
object owner;
25 private readonly
string creatorStackTrace;
26 private string lockStackTrace;
27 private LinkedList<WaitRec> noWriters =
new LinkedList<WaitRec>();
28 private LinkedList<WaitRec> noReadersOrWriters =
new LinkedList<WaitRec>();
29 private readonly
object synchObj =
new object();
30 private long token = 0;
31 private long start = 0;
32 private int nrReaders = 0;
33 private bool isWriting =
false;
34 private bool disposed =
false;
38 public TaskCompletionSource<bool> Pending;
39 public string StackTrace;
43 this.Pending =
new TaskCompletionSource<bool>();
46 this.StackTrace = Environment.StackTrace;
48 this.StackTrace =
null;
96 if (this.recordStackTraces)
97 this.creatorStackTrace = Environment.StackTrace;
99 this.creatorStackTrace =
null;
101 this.lockStackTrace =
null;
137 return this.nrReaders;
154 return this.nrReaders > 0;
171 return this.isWriting;
188 return this.nrReaders > 0 || this.isWriting;
205 return this.noReadersOrWriters.Count;
224 foreach (WaitRec Rec
in this.noReadersOrWriters)
226 if (!
string.IsNullOrEmpty(Rec.StackTrace))
231 Result.
Add(Rec.StackTrace);
236 return Result?.
ToArray() ?? Array.Empty<
string>();
251 else if (this.nrReaders > 0 || this.isWriting)
252 return watch.ElapsedTicks - this.start;
280 if (this.nrReaders <= 0)
281 throw new InvalidOperationException(
"Not in a reading state.");
297 throw new InvalidOperationException(
"Not in a writing state.");
312 if (this.nrReaders <= 0 && !this.isWriting)
313 throw new InvalidOperationException(
"Not in a reading or writing state.");
349 bool RecordStackTrace =
false;
357 if (this.nrReaders == 0)
359 this.start = watch.ElapsedTicks;
362 if (this.recordStackTraces)
363 RecordStackTrace =
true;
366 Result = ++this.nrReaders;
370 Wait =
new WaitRec(this.recordStackTraces);
371 this.noWriters.AddLast(Wait);
377 if (RecordStackTrace)
378 this.lockStackTrace = Environment.StackTrace;
384 await Wait.Pending.Task;
400 LinkedList<WaitRec> List =
null;
404 if (this.nrReaders <= 0)
405 throw new InvalidOperationException(
"Not in a reading state.");
408 if (this.nrReaders == 0)
413 if (this.recordStackTraces)
414 this.lockStackTrace =
null;
417 return Task.FromResult(this.nrReaders);
419 if (this.noReadersOrWriters.First is
null)
420 return Task.FromResult(0);
422 List = this.noReadersOrWriters;
423 this.noReadersOrWriters =
new LinkedList<WaitRec>();
426 foreach (WaitRec Rec
in List)
427 Rec.Pending.TrySetResult(
true);
429 return Task.FromResult(0);
444 DateTime Start = DateTime.UtcNow;
445 bool RecordStackTrace =
false;
453 if (this.nrReaders == 0)
455 this.start = watch.ElapsedTicks;
458 if (this.recordStackTraces)
459 RecordStackTrace =
true;
464 else if (Timeout <= 0)
468 Wait =
new WaitRec(this.recordStackTraces);
469 this.noWriters.AddLast(Wait);
475 if (RecordStackTrace)
476 this.lockStackTrace = Environment.StackTrace;
482 DateTime Now = DateTime.UtcNow;
485 using (Timer Timer =
new Timer((P) =>
487 Wait?.Pending.TrySetResult(
false);
489 },
null, Timeout,
System.Threading.Timeout.Infinite))
491 Result = await Wait.Pending.Task;
498 this.noWriters.Remove(Wait);
504 Timeout -= (int)((Now - Start).TotalMilliseconds + 0.5);
523 if (Cancel.CanBeCanceled)
526 bool RecordStackTrace =
false;
528 Cancel.Register(() =>
530 Wait?.Pending.TrySetResult(
false);
539 if (this.nrReaders == 0)
541 this.start = watch.ElapsedTicks;
544 if (this.recordStackTraces)
545 RecordStackTrace =
true;
550 else if (Cancel.IsCancellationRequested)
554 Wait =
new WaitRec(this.recordStackTraces);
555 this.noWriters.AddLast(Wait);
561 if (RecordStackTrace)
562 this.lockStackTrace = Environment.StackTrace;
568 bool Result = await Wait.Pending.Task;
574 this.noWriters.Remove(Wait);
602 bool RecordStackTrace =
false;
610 this.noWriters.Remove(Prev);
611 this.noReadersOrWriters.Remove(Prev);
614 if (this.nrReaders == 0 && !this.isWriting)
616 this.start = watch.ElapsedTicks;
618 this.isWriting =
true;
620 if (this.recordStackTraces)
621 RecordStackTrace =
true;
625 Wait =
new WaitRec(this.recordStackTraces);
626 this.noReadersOrWriters.AddLast(Wait);
627 this.noWriters.AddLast(Wait);
633 if (RecordStackTrace)
634 this.lockStackTrace = Environment.StackTrace;
640 await Wait.Pending.Task;
656 LinkedList<WaitRec> List =
null;
657 LinkedList<WaitRec> List2 =
null;
662 throw new InvalidOperationException(
"Not in a writing state.");
665 this.isWriting =
false;
668 if (this.recordStackTraces)
669 this.lockStackTrace =
null;
671 if (!(this.noReadersOrWriters.First is
null))
673 List = this.noReadersOrWriters;
674 this.noReadersOrWriters =
new LinkedList<WaitRec>();
677 if (!(this.noWriters.First is
null))
679 List2 = this.noWriters;
680 this.noWriters =
new LinkedList<WaitRec>();
686 foreach (WaitRec Rec
in List)
687 Rec.Pending.TrySetResult(
true);
690 if (!(List2 is
null))
692 foreach (WaitRec Rec
in List2)
693 Rec.Pending.TrySetResult(
true);
696 return Task.CompletedTask;
712 DateTime Start = DateTime.UtcNow;
713 bool RecordStackTrace =
false;
721 this.noWriters.Remove(Prev);
722 this.noReadersOrWriters.Remove(Prev);
725 if (this.nrReaders == 0 && !this.isWriting)
727 this.start = watch.ElapsedTicks;
729 this.isWriting =
true;
731 if (this.recordStackTraces)
732 RecordStackTrace =
true;
734 else if (Timeout <= 0)
738 Wait =
new WaitRec(this.recordStackTraces);
739 this.noWriters.AddLast(Wait);
740 this.noReadersOrWriters.AddLast(Wait);
746 if (RecordStackTrace)
747 this.lockStackTrace = Environment.StackTrace;
753 DateTime Now = DateTime.UtcNow;
756 using (Timer Timer =
new Timer((P) =>
758 Wait?.Pending.TrySetResult(
false);
760 },
null, Timeout,
System.Threading.Timeout.Infinite))
762 Result = await Wait.Pending.Task;
769 this.noWriters.Remove(Wait);
770 this.noReadersOrWriters.Remove(Wait);
776 Timeout -= (int)((Now - Start).TotalMilliseconds + 0.5);
796 if (Cancel.CanBeCanceled)
800 DateTime Start = DateTime.UtcNow;
801 bool RecordStackTrace =
false;
803 Cancel.Register(() =>
805 Prev?.Pending.TrySetResult(
false);
806 Wait?.Pending.TrySetResult(
false);
815 this.noWriters.Remove(Prev);
816 this.noReadersOrWriters.Remove(Prev);
819 if (this.nrReaders == 0 && !this.isWriting)
821 this.start = watch.ElapsedTicks;
823 this.isWriting =
true;
825 if (this.recordStackTraces)
826 RecordStackTrace =
true;
828 else if (Cancel.IsCancellationRequested)
832 Wait =
new WaitRec(this.recordStackTraces);
833 this.noWriters.AddLast(Wait);
834 this.noReadersOrWriters.AddLast(Wait);
840 if (RecordStackTrace)
841 this.lockStackTrace = Environment.StackTrace;
847 bool Result = await Wait.Pending.Task;
853 this.noWriters.Remove(Wait);
854 this.noReadersOrWriters.Remove(Wait);
877 LinkedList<WaitRec> List =
null;
878 LinkedList<WaitRec> List2 =
null;
883 this.isWriting =
false;
887 if (this.recordStackTraces)
888 this.lockStackTrace =
null;
890 if (!(this.noReadersOrWriters.First is
null))
892 List = this.noReadersOrWriters;
893 this.noReadersOrWriters =
new LinkedList<WaitRec>();
896 if (!(this.noWriters.First is
null))
898 List2 = this.noWriters;
899 this.noWriters =
new LinkedList<WaitRec>();
905 foreach (WaitRec Rec
in List)
906 Rec.Pending.TrySetResult(
false);
909 if (!(List2 is
null))
911 foreach (WaitRec Rec
in List2)
912 Rec.Pending.TrySetResult(
false);
915 return Task.CompletedTask;
927 this.disposed =
true;
A chunked list is a linked list of chunks of objects of type T .
void Add(T Item)
Adds an item to the collection.
T[] ToArray()
Returns an array containing all elements of the collection.
Represents an object that allows single concurrent writers but multiple concurrent readers....
string LockStackTrace
Stack trace from lock of object, if RecordStackTraces is true.
MultiReadSingleWriteObject(object Owner, bool RecordStackTraces)
Represents an object that allows single concurrent writers but multiple concurrent readers....
void AssertReading()
Throws an InvalidOperationException if the object is not in a reading state.
string[] QueuedStackTraces
Recorded stack traces waiting for access.
MultiReadSingleWriteObject()
Represents an object that allows single concurrent writers but multiple concurrent readers....
MultiReadSingleWriteObject(bool RecordStackTraces)
Represents an object that allows single concurrent writers but multiple concurrent readers....
virtual async Task< bool > TryBeginRead(int Timeout)
Waits, at most Timeout milliseconds, until object ready for reading. Each successful call to TryBegi...
virtual Task EndWrite()
Ends a writing session of the object. Must be called once for each call to BeginWrite or successful c...
int NrReaders
Number of concurrent readers.
bool Disposed
If object has been disposed.
void AssertReadingOrWriting()
Throws an InvalidOperationException if the object is not in a reading or writing state.
virtual Task Unlock()
Unlocks all locks on the object.
bool IsReading
If the object is in a reading state.
virtual async Task< bool > TryBeginRead(CancellationToken Cancel)
Waits until object ready for reading, or the attempt is cancelled. Each successful call to TryBeginRe...
bool IsWriting
If the object has a writer.
bool RecordStackTraces
If stack traces should be recorded when object is locked. Default value is true in DEBUG mode and fal...
long Token
Returns a token corresponding to the current lock. It is incremented at the start of a lock-cycle (wh...
double MillisecondsLocked
Number of milliseconds the object has been locked.
long TicksLocked
Number of ticks the object has been locked.
int QueueSize
Number of tasks waiting.
virtual async Task< bool > TryBeginWrite(int Timeout)
Waits, at most Timeout milliseconds, until object ready for writing. Each successful call to TryBegi...
virtual async Task BeginWrite()
Waits until object ready for writing. Each call to BeginWrite must be followed by exactly one call to...
string CreatorStackTrace
Stack trace from creation of object, if RecordStackTraces is true.
virtual Task< int > EndRead()
Ends a reading session of the object. Must be called once for each call to BeginRead or successful ca...
object Owner
Owner of object.
void AssertWriting()
Throws an InvalidOperationException if the object is not in a writing state.
virtual async Task< bool > TryBeginWrite(CancellationToken Cancel)
Waits until object ready for writing, or the attempt is cancelled. Each successful call to TryBeginWr...
bool IsReadingOrWriting
If the object is locked for reading or writing.
virtual async Task< int > BeginRead()
Waits until object ready for reading. Each call to BeginRead must be followed by exactly one call to ...
MultiReadSingleWriteObject(object Owner)
Represents an object that allows single concurrent writers but multiple concurrent readers....
virtual void Dispose()
IDisposable.Dispose
An interface for objects that allow single concurrent writers but multiple concurrent readers.