Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
AsyncQueue.cs
1using System;
3using System.Threading;
4using System.Threading.Tasks;
5using Waher.Events;
6
8{
14 public class AsyncQueue<T> : IDisposable
15 where T : class
16 {
17 private readonly LinkedList<Item> queue = new LinkedList<Item>();
18 private readonly LinkedList<TaskCompletionSource<T>> subscribers = new LinkedList<TaskCompletionSource<T>>();
19 private readonly TaskCompletionSource<bool> terminatedTask = new TaskCompletionSource<bool>();
20 private readonly object synchObj = new object();
21 private volatile int countItems = 0;
22 private volatile int countSubscribers = 0;
23 private bool disposed = false;
24 private bool terminated = false;
25 private bool empty = true;
26 private bool waiting = false;
27
32 public AsyncQueue()
33 {
34 }
35
36 private class Item
37 {
38 internal T Value { get; }
39 internal TaskCompletionSource<bool> Forwarded;
40
41 internal Item(T Value)
42 {
43 this.Value = Value;
44 this.Forwarded = null;
45 }
46
47 internal Item(T Value, TaskCompletionSource<bool> Forwarded)
48 {
49 this.Value = Value;
50 this.Forwarded = Forwarded;
51 }
52 }
53
57 public int CountItems
58 {
59 get
60 {
61 lock (this.synchObj)
62 {
63 return this.countItems;
64 }
65 }
66 }
67
72 {
73 get
74 {
75 lock (this.synchObj)
76 {
77 return this.countSubscribers;
78 }
79 }
80 }
81
85 public bool Empty
86 {
87 get
88 {
89 lock (this.synchObj)
90 {
91 return this.empty;
92 }
93 }
94 }
95
99 public bool Waiting
100 {
101 get
102 {
103 lock (this.synchObj)
104 {
105 return this.waiting;
106 }
107 }
108 }
109
115 [Obsolete("Use the Forward or Queue methods instead, for increased clarity.")]
116 public Task<bool> Add(T Item)
117 {
118 return this.Forward(Item, false);
119 }
120
126 [Obsolete("Use the Forward or Queue methods instead, for increased clarity.")]
127 public Task<bool> AddLast(T Item)
128 {
129 return this.Forward(Item, false);
130 }
131
137 [Obsolete("Use the Forward or Queue methods instead, for increased clarity.")]
138 public Task<bool> AddFirst(T Item)
139 {
140 return this.Forward(Item, true);
141 }
142
149 [Obsolete("Use the Forward or Queue methods instead, for increased clarity.")]
150 public Task<bool> Add(T Item, bool First)
151 {
152 return this.Forward(Item, First);
153 }
154
160 public void Queue(T Item)
161 {
162 this.Queue(Item, false);
163 }
164
170 public void QueueLast(T Item)
171 {
172 this.Queue(Item, false);
173 }
174
180 public void QueueFirst(T Item)
181 {
182 this.Queue(Item, true);
183 }
184
191 public void Queue(T Item, bool First)
192 {
193 EventHandler h = null;
194
195 lock (this.synchObj)
196 {
197 if (this.terminated || this.disposed)
198 return;
199
200 if (this.subscribers.First is null)
201 {
202 Item Record = new Item(Item);
203
204 if (First)
205 this.queue.AddFirst(Record);
206 else
207 this.queue.AddLast(Record);
208
209 this.countItems++;
210 if (this.empty)
211 {
212 this.empty = false;
213 h = this.OnNotEmpty;
214 }
215 }
216 else
217 {
218 TaskCompletionSource<T> Waiter = this.subscribers.First.Value;
219 this.subscribers.RemoveFirst();
220 this.countSubscribers--;
221 if (this.countSubscribers <= 0)
222 {
223 this.waiting = false;
224 h = this.OnNotWaiting;
225 }
226
227 Task.Run(() => Waiter.TrySetResult(Item)); // Ensures waiting logic not interrupting current logic.
228 }
229 }
230
231 h?.Raise(this, EventArgs.Empty);
232 }
233
239 public Task<bool> Forward(T Item)
240 {
241 return this.Forward(Item, false);
242 }
243
249 public Task<bool> ForwardLast(T Item)
250 {
251 return this.Forward(Item, false);
252 }
253
259 public Task<bool> ForwardFirst(T Item)
260 {
261 return this.Forward(Item, true);
262 }
263
270 public Task<bool> Forward(T Item, bool First)
271 {
272 EventHandler h = null;
273 Task<bool> Result;
274
275 lock (this.synchObj)
276 {
277 if (this.terminated || this.disposed)
278 return Task.FromResult(false);
279
280 if (this.subscribers.First is null)
281 {
282 TaskCompletionSource<bool> Forwarded = new TaskCompletionSource<bool>();
283
284 Item Record = new Item(Item, Forwarded);
285
286 if (First)
287 this.queue.AddFirst(Record);
288 else
289 this.queue.AddLast(Record);
290
291 this.countItems++;
292 if (this.empty)
293 {
294 this.empty = false;
295 h = this.OnNotEmpty;
296 }
297
298 Result = Record.Forwarded.Task;
299 }
300 else
301 {
302 TaskCompletionSource<T> Waiter = this.subscribers.First.Value;
303 this.subscribers.RemoveFirst();
304 this.countSubscribers--;
305 if (this.countSubscribers <= 0)
306 {
307 this.waiting = false;
308 h = this.OnNotWaiting;
309 }
310
311 Waiter.TrySetResult(Item);
312
313 Result = Task.FromResult(true);
314 }
315 }
316
317 h?.Raise(this, EventArgs.Empty);
318
319 return Result;
320 }
321
327 public Task<T> Wait()
328 {
329 return this.DoWait(CancellationToken.None, null);
330 }
331
332
339 public Task<T> Wait(int Timeout)
340 {
341 return this.DoWait(CancellationToken.None, Timeout);
342 }
343
351 public Task<T> Wait(CancellationToken Cancel)
352 {
353 return this.DoWait(Cancel, null);
354 }
355
364 public Task<T> Wait(CancellationToken Cancel, int Timeout)
365 {
366 return this.DoWait(Cancel, Timeout);
367 }
368
377 private Task<T> DoWait(CancellationToken Cancel, int? Timeout)
378 {
379 EventHandler h = null;
380 Task<T> Result;
381 Item Record;
382
383 if (Timeout.HasValue && Timeout.Value <= 0)
384 throw new ArgumentException("Timeout must be positive.", nameof(Timeout));
385
386 lock (this.synchObj)
387 {
388 if (this.queue.First is null)
389 {
390 if (this.disposed)
391 return Task.FromResult<T>(null);
392
393 TaskCompletionSource<T> Item = new TaskCompletionSource<T>();
394
395 if (Cancel.CanBeCanceled)
396 {
397 Cancel.Register(() => Item.TrySetResult(null));
398 if (Cancel.IsCancellationRequested)
399 return Task.FromResult<T>(null);
400 }
401
402 this.subscribers.AddLast(Item);
403 this.countSubscribers++;
404 if (!this.waiting)
405 {
406 this.waiting = true;
407 h = this.OnWaiting;
408 }
409
410 Result = Item.Task;
411
412 if (Timeout.HasValue)
413 {
414 Task.Delay(Timeout.Value).ContinueWith((_) =>
415 {
416 EventHandler h2 = null;
417
418 lock (this.synchObj)
419 {
420 if (!this.disposed && this.subscribers.Remove(Item))
421 {
422 this.countSubscribers--;
423 if (this.countSubscribers <= 0)
424 {
425 this.waiting = false;
426 h2 = this.OnNotWaiting;
427 }
428
429 Item.TrySetResult(null);
430 }
431 }
432
433 h2?.Raise(this, EventArgs.Empty);
434
435 return Task.CompletedTask;
436 });
437 }
438 }
439 else
440 {
441 Record = this.queue.First.Value;
442 this.queue.RemoveFirst();
443 this.countItems--;
444 if (this.countItems <= 0)
445 {
446 this.empty = true;
447 h = this.OnEmpty;
448 }
449
450 Record.Forwarded?.TrySetResult(true);
451
452 if (this.terminated && this.queue.First is null)
453 {
454 this.disposed = true;
455 this.terminatedTask.TrySetResult(true);
456 }
457
458 Result = Task.FromResult(Record.Value);
459 }
460 }
461
462 h?.Raise(this, EventArgs.Empty);
463
464 return Result;
465 }
466
473 public bool TryPeekItem(out T Item)
474 {
475 return this.TryGetItem(false, out Item);
476 }
477
484 public bool TryGetItem(out T Item)
485 {
486 return this.TryGetItem(true, out Item);
487 }
488
489 private bool TryGetItem(bool Remove, out T Item)
490 {
491 EventHandler h = null;
492 bool Result;
493
494 lock (this.synchObj)
495 {
496 if (this.disposed || this.queue.First is null)
497 {
498 Item = null;
499 Result = false;
500 }
501 else
502 {
503 Item Record = this.queue.First.Value;
504 Item = Record.Value;
505
506 if (Remove)
507 {
508 this.queue.RemoveFirst();
509 this.countItems--;
510 if (this.countItems <= 0)
511 {
512 this.empty = true;
513 h = this.OnEmpty;
514 }
515
516 Record.Forwarded?.TrySetResult(true);
517
518 if (this.terminated && this.queue.First is null)
519 {
520 this.disposed = true;
521 Result = false;
522 }
523 else
524 Result = true;
525 }
526 else
527 Result = true;
528 }
529 }
530
531 h?.Raise(this, EventArgs.Empty);
532
533 return Result;
534 }
535
539 public void Dispose()
540 {
541 lock (this.synchObj)
542 {
543 if (this.disposed)
544 return;
545
546 this.disposed = true;
547 this.terminated = true;
548
549 foreach (Item Record in this.queue)
550 Record.Forwarded?.TrySetResult(false);
551
552 this.queue.Clear();
553 this.countItems = 0;
554
555 foreach (TaskCompletionSource<T> Task in this.subscribers)
556 Task.TrySetResult(null);
557
558 this.subscribers.Clear();
559 this.countSubscribers = 0;
560 }
561
562 this.RaiseDisposed();
563 }
564
570 public Task Terminate()
571 {
572 lock (this.synchObj)
573 {
574 this.terminated = true;
575
576 if (this.queue.First is null)
577 {
578 this.disposed = true;
579
580 foreach (Item Record in this.queue)
581 Record.Forwarded?.TrySetResult(false);
582
583 this.queue.Clear();
584 this.countItems = 0;
585
586 foreach (TaskCompletionSource<T> Task in this.subscribers)
587 Task.TrySetResult(null);
588
589 this.subscribers.Clear();
590 this.countSubscribers = 0;
591 }
592 else
593 return this.terminatedTask.Task;
594 }
595
596 this.RaiseDisposed();
597 return this.terminatedTask.Task;
598 }
599
603 public event EventHandler Disposed = null;
604
608 public event EventHandler OnEmpty = null;
609
613 public event EventHandler OnNotEmpty = null;
614
618 public event EventHandler OnWaiting = null;
619
623 public event EventHandler OnNotWaiting = null;
624
625 private void RaiseDisposed()
626 {
627 try
628 {
629 this.terminatedTask.TrySetResult(true);
630
631 this.Disposed.Raise(this, EventArgs.Empty);
632 }
633 catch (Exception ex)
634 {
635 Log.Exception(ex);
636 }
637 }
638 }
639}
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1657
Asynchronous First-in-First-out (FIFO) Queue, for use when transporting items of type T between task...
Definition: AsyncQueue.cs:16
Task Terminate()
Terminates the queue, allowing subscribers to get queued items, but disallows new items to be added....
Definition: AsyncQueue.cs:570
Task< bool > AddFirst(T Item)
Adds an item first to the queue.
Definition: AsyncQueue.cs:138
Task< bool > Forward(T Item, bool First)
Processes an item by adding it to the queue.
Definition: AsyncQueue.cs:270
Task< bool > Forward(T Item)
Processes an item by adding it last in the queue.
Definition: AsyncQueue.cs:239
Task< bool > ForwardLast(T Item)
Processes an item by adding it last in the queue.
Definition: AsyncQueue.cs:249
void QueueLast(T Item)
Queues an item for processing by adding it last in the queue. No information is returned wether the i...
Definition: AsyncQueue.cs:170
EventHandler OnNotEmpty
Event raised when Empty changed to false.
Definition: AsyncQueue.cs:613
EventHandler OnWaiting
Event raised when Waiting changed to true.
Definition: AsyncQueue.cs:618
EventHandler OnEmpty
Event raised when Empty changed to true.
Definition: AsyncQueue.cs:608
Task< T > Wait()
Waits indefinitely (or until queue is disposed) for an item to be available. If Queue is disposed,...
Definition: AsyncQueue.cs:327
bool Waiting
If there are subscribers waiting for work.
Definition: AsyncQueue.cs:100
int CountSubscribers
Number of subscribers waiting for items.
Definition: AsyncQueue.cs:72
void Queue(T Item)
Queues an item for processing by adding it last in the queue. No information is returned wether the i...
Definition: AsyncQueue.cs:160
void QueueFirst(T Item)
Queues an item for processing by adding it first in the queue. No information is returned wether the ...
Definition: AsyncQueue.cs:180
void Queue(T Item, bool First)
Queues an item for processing. No information is returned wether the item is forwarded for processing...
Definition: AsyncQueue.cs:191
Task< bool > ForwardFirst(T Item)
Processes an item by adding it first in the queue.
Definition: AsyncQueue.cs:259
bool TryGetItem(out T Item)
Tries to get a queued item, if found. If not, the method returns immediately with a null item.
Definition: AsyncQueue.cs:484
Task< T > Wait(int Timeout)
Waits indefinitely (or until queue is disposed) for an item to be available. If Queue is disposed,...
Definition: AsyncQueue.cs:339
bool Empty
If the queue is empty.
Definition: AsyncQueue.cs:86
void Dispose()
IDisposable.Dispose
Definition: AsyncQueue.cs:539
EventHandler Disposed
Event raised when queue has been disposed.
Definition: AsyncQueue.cs:603
EventHandler OnNotWaiting
Event raised when Waiting changed to false.
Definition: AsyncQueue.cs:623
Task< bool > AddLast(T Item)
Adds an item last to the queue.
Definition: AsyncQueue.cs:127
AsyncQueue()
Asynchronous Queue, for use when transporting items of class T between tasks.
Definition: AsyncQueue.cs:32
Task< T > Wait(CancellationToken Cancel, int Timeout)
Waits indefinitely (or until queue is disposed or task cancelled) for an item to be available....
Definition: AsyncQueue.cs:364
Task< bool > Add(T Item)
Adds an item last to the queue.
Definition: AsyncQueue.cs:116
Task< bool > Add(T Item, bool First)
Adds an item to the queue.
Definition: AsyncQueue.cs:150
int CountItems
Number of items in queue.
Definition: AsyncQueue.cs:58
bool TryPeekItem(out T Item)
Tries to get a queued item, if found. If not, the method returns immediately with a null item.
Definition: AsyncQueue.cs:473
Task< T > Wait(CancellationToken Cancel)
Waits indefinitely (or until queue is disposed or task cancelled) for an item to be available....
Definition: AsyncQueue.cs:351
Definition: ImplTypes.g.cs:58