Skip to content

Commit 5c3ca27

Browse files
committed
Replace SetEnding and ending state with Clear.
1 parent f7298ee commit 5c3ca27

4 files changed

Lines changed: 57 additions & 130 deletions

File tree

DependencyQueue.Tests/DependencyQueueTests.cs

Lines changed: 27 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -218,18 +218,6 @@ public void Enqueue_DuplicateEntry()
218218
queue.ShouldHaveTopic("a", providedBy: [entryA0, entryA1]);
219219
}
220220

221-
[Test]
222-
public void Enqueue_Ending()
223-
{
224-
using var queue = new Queue();
225-
226-
queue.SetEnding();
227-
228-
Should.Throw<InvalidOperationException>(
229-
() => queue.Enqueue("a", value: new())
230-
);
231-
}
232-
233221
[Test]
234222
public void Enqueue_Disposed()
235223
{
@@ -315,18 +303,6 @@ public void Validate_Cycle_Indirect()
315303
error.RequiredTopic.Name.ShouldBe("a");
316304
}
317305

318-
[Test]
319-
public void Validate_Ending()
320-
{
321-
using var queue = new Queue();
322-
323-
queue.SetEnding();
324-
325-
// TODO: Error?
326-
// Allowed but not very useful
327-
queue.Validate().ShouldBeEmpty();
328-
}
329-
330306
[Test]
331307
public void Validate_Disposed()
332308
{
@@ -359,23 +335,6 @@ public void Dequeue_Empty()
359335
queue.Dequeue().ShouldBeNull();
360336
}
361337

362-
[Test]
363-
public void Dequeue_Ending()
364-
{
365-
using var queue = new Queue();
366-
367-
var entry = queue.Enqueue("a", value: new());
368-
369-
queue.ShouldBeValid();
370-
queue.SetEnding();
371-
372-
queue.Dequeue().ShouldBeNull();
373-
374-
queue.ShouldHaveReadyEntries(entry);
375-
queue.ShouldHaveTopicCount(1);
376-
queue.ShouldHaveTopic("a", providedBy: [entry]);
377-
}
378-
379338
[Test]
380339
public void Dequeue_Disposed()
381340
{
@@ -563,23 +522,6 @@ public async Task DequeueAsync_Initial()
563522
(await queue.DequeueAsync()).ShouldBeNull();
564523
}
565524

566-
[Test]
567-
public async Task DequeueAsync_Ending()
568-
{
569-
using var queue = new Queue();
570-
571-
var entry = queue.Enqueue("a", value: new());
572-
573-
queue.ShouldBeValid();
574-
queue.SetEnding();
575-
576-
(await queue.DequeueAsync()).ShouldBeNull();
577-
578-
queue.ShouldHaveReadyEntries(entry);
579-
queue.ShouldHaveTopicCount(1);
580-
queue.ShouldHaveTopic("a", providedBy: [entry]);
581-
}
582-
583525
[Test]
584526
public async Task DequeueAsync_Disposed()
585527
{
@@ -767,21 +709,6 @@ public void Complete_NullEntry()
767709
e.ParamName.ShouldBe("entry");
768710
}
769711

770-
[Test]
771-
public void Complete_Ending()
772-
{
773-
using var queue = new Queue();
774-
775-
var entry = queue.Enqueue("a", value: new());
776-
777-
queue.ShouldBeValid();
778-
queue.Dequeue().ShouldBeSameAs(entry);
779-
queue.SetEnding();
780-
781-
// Allowed
782-
queue.Complete(entry);
783-
}
784-
785712
[Test]
786713
public void Complete_Disposed()
787714
{
@@ -867,6 +794,33 @@ public void Complete_NotEnqueued()
867794
queue.ShouldHaveTopic("y", providedBy: [entryY]);
868795
}
869796

797+
[Test]
798+
public void Clear_Ok()
799+
{
800+
using var queue = new Queue();
801+
802+
var entry = queue.Enqueue("a", value: new());
803+
804+
queue.ShouldBeValid();
805+
queue.Clear();
806+
807+
queue.ShouldNotHaveReadyEntries();
808+
queue.ShouldHaveTopicCount(0);
809+
queue.Dequeue().ShouldBeNull();
810+
}
811+
812+
[Test]
813+
public void Clear_Disposed()
814+
{
815+
var queue = new Queue();
816+
817+
queue.Dispose();
818+
819+
Should.Throw<ObjectDisposedException>(
820+
() => queue.Clear()
821+
);
822+
}
823+
870824
[Test]
871825
public void Dispose_Managed()
872826
{

DependencyQueue/DependencyQueue.cs

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ public class DependencyQueue<T> : IDisposable
2828
// Whether queue state is valid
2929
private bool _isValid;
3030

31-
// Whether queue processing is terminating
32-
private bool _isEnding;
33-
3431
/// <summary>
3532
/// Initializes a new <see cref="DependencyQueue{T}"/> instance,
3633
/// optionally with the specified topic name comparer.
@@ -159,9 +156,6 @@ internal void Enqueue(DependencyQueueEntry<T> entry)
159156

160157
using var @lock = _monitor.Acquire();
161158

162-
if (_isEnding)
163-
throw Errors.QueueEnded();
164-
165159
foreach (var name in entry.Provides)
166160
GetOrAddTopic(name).ProvidedBy.Add(entry);
167161

@@ -233,10 +227,6 @@ internal void Enqueue(DependencyQueueEntry<T> entry)
233227

234228
for (;;)
235229
{
236-
// Check if processing is ending
237-
if (_isEnding)
238-
return null;
239-
240230
// Check if all topics (and thus all entries) are completed
241231
if (_topics.Count is 0)
242232
return null;
@@ -357,10 +347,6 @@ internal void Enqueue(DependencyQueueEntry<T> entry)
357347

358348
for (;;)
359349
{
360-
// Check if processing is ending
361-
if (_isEnding)
362-
return null;
363-
364350
// Check if all topics (and thus all entries) are completed
365351
if (_topics.Count is 0)
366352
return null;
@@ -468,20 +454,19 @@ public void Complete(DependencyQueueEntry<T> entry)
468454
}
469455

470456
/// <summary>
471-
/// Transitions the queue to the ending state.
457+
/// Removes all entries from the queue.
472458
/// </summary>
473459
/// <remarks>
474-
/// <para>
475-
/// In the ending state, no new entries can be enqueued, and dequeue
476-
/// operations return <see langword="null"/>.
477-
/// </para>
478-
/// <para>
479-
/// This method is thread-safe.
480-
/// </para>
460+
/// This method is thread-safe.
481461
/// </remarks>
482-
public void SetEnding()
462+
public void Clear()
483463
{
484-
_isEnding = true;
464+
using var @lock = _monitor.Acquire();
465+
466+
_ready .Clear();
467+
_topics.Clear();
468+
_isValid = true;
469+
485470
_monitor.PulseAll();
486471
}
487472

DependencyQueue/Errors.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ internal static Exception ObjectDisposed(string? name)
2626
internal static Exception CollectionEmpty()
2727
=> new InvalidOperationException("The collection is empty.");
2828

29-
internal static Exception QueueEnded()
30-
=> new InvalidOperationException("The queue is ended and does not accept new entries.");
31-
3229
internal static Exception EnumeratorNoCurrentItem()
3330
=> new InvalidOperationException(
3431
"The enumerator is positioned before the first element " +

README.md

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,16 @@ constructor.
103103

104104
### Validation
105105

106-
Upon construction or after enqueueing an item, a `DependencyQueue<T>` instance
107-
is in an unvalidated state. Before any items can be dequeued, the queue must
108-
be validated. Do that by calling `Validate()`.
106+
After enqueueing an item, a `DependencyQueue<T>` instance is in an unvalidated
107+
state. Before any items can be dequeued, the queue must be validated. Do that
108+
by calling `Validate()`.
109109

110110
```csharp
111111
var errors = queue.Validate();
112112
```
113113

114114
If the queue is valid, `Validate()` returns an empty list of error objects.
115-
Otherwiws, the list describes the problems found.
115+
Otherwise, the list describes the problems found.
116116

117117
The web of dependencies between entries — the 'dependency graph' — can be
118118
invalid in two ways.
@@ -126,17 +126,15 @@ invalid in two ways.
126126

127127
### Dequeueing Items
128128

129-
There are several ways to dequeue items from a `DependencyQueue<T>` instance.
130-
All of them require the queue to be valid.
131-
132-
The simplest way is to call `TryDequeue()` or `TryDequeueAsync()`, which return
133-
the next entry in the queue or null if the queue is empty.
129+
To dequeue entries from a validated queue, call one of the dequeue methods
130+
`Dequeue()` or `DequeueAsync()`. Both methods yield the next entry in the
131+
queue, or `null` if the queue is empty.
134132

135133
```csharp
136-
var entry = queue.TryDequeue();
134+
var entry = queue.Dequeue();
137135
```
138136
```csharp
139-
await var entry = queue.TryDequeueAsync(cancellation: cancellationToken);
137+
var entry = await queue.DequeueAsync(cancellation: cancellationToken);
140138
```
141139

142140
The item is available in the `Value` property of the returned entry.
@@ -148,19 +146,19 @@ call `Complete()` to inform the queue.
148146
queue.Complete(entry);
149147
```
150148

151-
`TryDequeue()`, `TryDequeueAsync()`, and `Complete()` are thread-safe. For
152-
full thread safety information, see the [Thread Safety](#thread-safety) section
149+
`Dequeue()`, `DequeueAsync()`, and `Complete()` are thread-safe. For full
150+
thread safety information, see the [Thread Safety](#thread-safety) section
153151
below.
154152

155-
All dequeue methods support an optional predicate parameter. If the caller
153+
The dequeue methods support an optional predicate parameter. If the caller
156154
provides a predicate, the queue tests each ready-to-dequeue item against the
157155
predicate and yields the first entry for which the predicate returns `true`.
158156
If the predicate does not return `true` for any ready-to-dequeue item, then
159157
the dequeue method blocks until an item becomes available that does satisfy the
160158
predicate.
161159

162160
```csharp
163-
await var entry = queue.TryDequeueAsync(
161+
var entry = await queue.DequeueAsync(
164162
item => MyCustomPredicate(item),
165163
cancellationToken
166164
);
@@ -188,26 +186,19 @@ _ = view.Topics["Foo"].RequiredBy; // Entries that require topic "Foo"
188186

189187
### States
190188

191-
A `DependencyQueue<T>` instance has four possible states:
189+
A `DependencyQueue<T>` instance has three possible states:
192190

193191
- **Unvalidated:**
194192
- The queue has not been validated or was found to be invalid.
195193
- Items can be enqueued.
196194
- Dequeue methods will throw `InvalidOperationException`.
197-
- Call `Validate()` to transition to the **Valid** state.
198-
- Call `SetEnding()` to transition to the **Ending** state.
199-
- Call `Dispose()` to transition to the **Disposed** state.
195+
- Call `Validate()` or `Clear()` to transition to the **Valid** state.
196+
- Call `Dispose()` to transition to the **Disposed** state.
200197
- **Valid:**
201198
- The queue was found to be valid.
202199
- Items can be dequeued.
203200
- Enqueuing a new item transitions back to the **Unvalidated** state.
204-
- Call `SetEnding()` to transition to the **Ending** state.
205-
- Call `Dispose()` to transition to the **Disposed** state.
206-
- **Ending:**
207-
- Queue processing is ending early.
208-
- Items can be enqueued, but will be ignored.
209-
- Dequeue methods will return `null` immediately.
210-
- Call `Dispose()` to transition to the **Disposed** state.
201+
- Call `Dispose()` to transition to the **Disposed** state.
211202
- **Disposed:**
212203
- The queue has been disposed and is no longer unsable.
213204
- Most methods will throw `ObjectDisposedException`.
@@ -224,15 +215,15 @@ Most methods of `DependencyQueue<T>` are thread-safe. Specifically:
224215

225216
- The `Validate()` method is thread-safe.
226217

227-
- The dequeue methods (`TryDequeue()`, `TryDequeueAsync()`, and `Complete()`)
218+
- The dequeue methods (`Dequeue()`, `DequeueAsync()`, and `Complete()`)
228219
are thread-safe.
229220

230221
- The inspection methods (`Inspect()` and `InspectAsync()`) are thread-safe, as
231222
are the objects they return.
232223

233-
- The `SetEnding()` method is thread-safe.
224+
- The `Clear()` method is thread-safe.
234225

235-
- The `Dispose()` is <strong>Not</strong> thread-safe.
226+
- The `Dispose()` method is <strong>NOT</strong> thread-safe.
236227

237228
## Examples
238229

@@ -286,7 +277,7 @@ if (errors.Any())
286277
throw new InvalidBurgerException(errors);
287278

288279
// Now build the burger
289-
while (queue.TryDequeue() is { } entry)
280+
while (queue.Dequeue() is { } entry)
290281
{
291282
Console.WriteLine($"Executing: {entry.Name}");
292283

0 commit comments

Comments
 (0)