Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
DatabaseQueue.cs
1using System;
3using System.Threading;
4using System.Threading.Tasks;
5using Waher.Events;
8
10{
15 {
16 private readonly LinkedList<TaskCompletionSource<object>> waitingDequeuers = new LinkedList<TaskCompletionSource<object>>();
17 private readonly SemaphoreSlim semaphore;
18 private readonly Profiler profiler;
19 private readonly bool profiling;
20 private readonly string queueName;
21 private readonly ProfilerThread enqueueThread;
22 private readonly ProfilerThread dequeueThread;
23 private bool disposed = false;
24
29 public DatabaseQueue(string QueueName)
30 : this(QueueName, null)
31 {
32 }
33
40 {
41 this.queueName = QueueName;
42 this.profiler = Profiler;
43 this.profiling = !(this.profiler is null);
44 this.semaphore = new SemaphoreSlim(1);
45
46 if (this.profiling)
47 {
48 this.enqueueThread = this.profiler.CreateThread("Enqueue(" + this.queueName + ")", ProfilerThreadType.Binary);
49 this.dequeueThread = this.profiler.CreateThread("Dequeue(" + this.queueName + ")", ProfilerThreadType.Binary);
50
51 this.enqueueThread.Start();
52 this.dequeueThread.Start();
53 }
54 else
55 {
56 this.enqueueThread = null;
57 this.dequeueThread = null;
58 }
59 }
60
64 public string QueueName => this.queueName;
65
70 public async Task<int> GetNrDequeuers()
71 {
72 await this.semaphore.WaitAsync();
73 try
74 {
75 return this.waitingDequeuers.Count;
76 }
77 finally
78 {
79 this.semaphore.Release();
80 }
81 }
82
88 public Task<int> GetNrEnqueuers() => Task.FromResult(0);
89
93 [Obsolete("Use DisposeAsync instead.")]
94 public void Dispose()
95 {
96 this.DisposeAsync().Wait();
97 }
98
102 public async Task DisposeAsync()
103 {
104 if (!this.disposed)
105 {
106 this.disposed = true;
107
108 await this.semaphore.WaitAsync();
109
110 foreach (TaskCompletionSource<object> T in this.waitingDequeuers)
111 T.TrySetResult(null);
112
113 this.waitingDequeuers.Clear();
114
115 if (this.profiling)
116 {
117 this.enqueueThread.Stop();
118 this.dequeueThread.Stop();
119 }
120 }
121 }
122
128 public Task<bool> Enqueue(object Item)
129 {
130 return this.Enqueue(Item, int.MaxValue);
131 }
132
139 public async Task<bool> Enqueue(object Item, int TimeoutMilliseconds)
140 {
141 if (Item is null)
142 throw new ArgumentNullException(nameof(Item));
143
144 if (this.profiling)
145 this.enqueueThread.High();
146
147 DateTime StartUtc = DateTime.UtcNow;
148 TaskCompletionSource<bool> Wait = null;
149 bool Forwarded;
150
151 do
152 {
153 if (this.disposed)
154 {
155 if (this.profiling)
156 this.enqueueThread.Low();
157
158 return false;
159 }
160
161 if (!(Wait is null))
162 {
163 int Milliseconds = (int)(TimeoutMilliseconds - DateTime.UtcNow.Subtract(StartUtc).TotalMilliseconds);
164 if (Milliseconds < 0)
165 {
166 if (this.profiling)
167 this.enqueueThread.Low();
168
169 return false;
170 }
171
172 _ = Task.Delay(Milliseconds).ContinueWith((_) =>
173 {
174 Wait?.TrySetResult(false);
175 return Task.CompletedTask;
176 });
177
178 if (!await Wait.Task || this.disposed)
179 {
180 if (this.profiling)
181 this.enqueueThread.Low();
182
183 return false;
184 }
185
186 Wait = null;
187 }
188
189 await this.semaphore.WaitAsync();
190 try
191 {
192 LinkedListNode<TaskCompletionSource<object>> First = this.waitingDequeuers.First;
193
194 if (First is null)
195 {
197 {
198 QueueName = this.queueName,
199 CreatedUtc = DateTime.UtcNow,
200 Content = Item
201 };
202
204 return true;
205 }
206 else
207 {
208 this.waitingDequeuers.RemoveFirst();
209 Forwarded = First.Value.TrySetResult(Item);
210 }
211 }
212 finally
213 {
214 this.semaphore.Release();
215 }
216 }
217 while (!Forwarded);
218
219 if (this.profiling)
220 this.enqueueThread.Low();
221
222 return true;
223 }
224
232 public Task<object> Dequeue()
233 {
234 return this.Dequeue(int.MaxValue, true);
235 }
236
237
243 public Task<object> TryDequeue()
244 {
245 return this.Dequeue(0, true);
246 }
247
257 public Task<object> Dequeue(int TimeoutMilliseconds)
258 {
259 return this.Dequeue(TimeoutMilliseconds, true);
260 }
261
267 public Task<object> Peek()
268 {
269 return this.Dequeue(0, false);
270 }
271
282 private async Task<object> Dequeue(int TimeoutMilliseconds, bool RemoveItem)
283 {
284 if (TimeoutMilliseconds < 0)
285 throw new ArgumentOutOfRangeException(nameof(TimeoutMilliseconds), "Value must be non-negative.");
286
287 if (this.disposed)
288 return null;
289
290 if (this.profiling)
291 this.dequeueThread.High();
292
293 bool Released = false;
294
295 await this.semaphore.WaitAsync();
296 try
297 {
298 if (RemoveItem)
299 {
300 IEnumerable<QueuedItem> Items = await Database.FindDelete<QueuedItem>(0, 1,
301 new FilterFieldEqualTo("QueueName", this.queueName),
302 "CreatedUtc");
303
304 foreach (QueuedItem Item in Items)
305 {
306 if (this.profiling)
307 this.dequeueThread.Low();
308
309 return Item.Content;
310 }
311 }
312 else
313 {
314 QueuedItem Item = await Database.FindFirstIgnoreRest<QueuedItem>(
315 new FilterFieldEqualTo("QueueName", this.queueName),
316 "CreatedUtc");
317
318 if (!(Item is null))
319 {
320 if (this.profiling)
321 this.dequeueThread.Low();
322
323 return Item.Content;
324 }
325 }
326
327 if (TimeoutMilliseconds == 0)
328 {
329 if (this.profiling)
330 this.dequeueThread.Low();
331
332 return null;
333 }
334
335 TaskCompletionSource<object> Waiter = new TaskCompletionSource<object>();
336 LinkedListNode<TaskCompletionSource<object>> Node = this.waitingDequeuers.AddLast(Waiter);
337
338 this.semaphore.Release();
339 Released = true;
340
341 _ = Task.Delay(TimeoutMilliseconds).ContinueWith(async (_) =>
342 {
343 try
344 {
345 await this.semaphore.WaitAsync();
346 try
347 {
348 if (!(Node.List is null))
349 this.waitingDequeuers.Remove(Node);
350 }
351 finally
352 {
353 this.semaphore.Release();
354 }
355
356 Waiter.TrySetResult(null);
357 }
358 catch (Exception ex)
359 {
360 Log.Exception(ex);
361 }
362 });
363
364 object Result = await Waiter.Task;
365
366 if (!RemoveItem)
367 {
368 QueuedItem QueuedItem = new QueuedItem()
369 {
370 QueueName = this.queueName,
371 CreatedUtc = DateTime.UtcNow,
372 Content = Result
373 };
374
375 await Database.Insert(QueuedItem);
376 }
377
378 if (this.profiling)
379 this.dequeueThread.Low();
380
381 return Result;
382 }
383 finally
384 {
385 if (!Released)
386 this.semaphore.Release();
387 }
388 }
389
393 public async Task Clear()
394 {
395 await this.semaphore.WaitAsync();
396 try
397 {
399 new FilterFieldEqualTo("QueueName", this.queueName),
400 "CreatedUtc");
401 }
402 finally
403 {
404 this.semaphore.Release();
405 }
406 }
407
408 }
409}
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
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
static Task< IEnumerable< object > > FindDelete(string Collection, params string[] SortOrder)
Finds objects in a given collection and deletes them in the same atomic operation.
Definition: Database.cs:1442
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:1291
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:97
This filter selects objects that have a named field equal to a given value.
Queue persisted into the database.
void Dispose()
Disposes the connection
Task< int > GetNrEnqueuers()
Gets the number of enqueuers waiting for space to be available to enqueue new items.
Task< object > Peek()
Returns the next item available to be dequeued, without dequeueing it. If an item is not available,...
async Task< bool > Enqueue(object Item, int TimeoutMilliseconds)
Enqueues an item into the queue.
DatabaseQueue(string QueueName, Profiler Profiler)
Queue persisted into the database.
Task< object > Dequeue(int TimeoutMilliseconds)
Dequeue an item from the queue. The task will wait for an item to be dequeued, or,...
Task< object > Dequeue()
Dequeue an item from the queue. The task will wait for an item to be dequeued, or,...
async Task Clear()
Clears the queue.
Task< object > TryDequeue()
Tries to dequeue an item from the queue, if one exists. If an item is not available,...
Task< bool > Enqueue(object Item)
Enqueues an item into the queue.
DatabaseQueue(string QueueName)
Queue persisted into the database.
async Task DisposeAsync()
Disposes of the object, asynchronously.
async Task< int > GetNrDequeuers()
Gets the number of dequeuers waiting for items to be queued.
Represents one item in a queue.
Definition: QueuedItem.cs:13
object Content
Object content of item.
Definition: QueuedItem.cs:45
Class that keeps track of events and timing.
Definition: Profiler.cs:68
Class that keeps track of events and timing for one thread.
Inteface for persisted queues.
Definition: ImplTypes.g.cs:58
ProfilerThreadType
Type of profiler thread.