Skip to content

Commit ec68850

Browse files
committed
Fix RequestContainer Task issue
Fix RequestContainer State issue Add IEnumerable to RequestContainer
1 parent 5a1110b commit ec68850

File tree

4 files changed

+118
-61
lines changed

4 files changed

+118
-61
lines changed

Requests.sln

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 17
44
VisualStudioVersion = 17.4.33122.133
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Requests", "Requests\Requests.csproj", "{95D249D8-C261-43E8-941B-386E78BCC381}"
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Requests", "Requests\Requests.csproj", "{95D249D8-C261-43E8-941B-386E78BCC381}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTest", "UnitTest\UnitTest.csproj", "{CD416881-1C3A-4A8A-84F4-F9FA2F884602}"
79
EndProject
810
Global
911
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,6 +17,10 @@ Global
1517
{95D249D8-C261-43E8-941B-386E78BCC381}.Debug|Any CPU.Build.0 = Debug|Any CPU
1618
{95D249D8-C261-43E8-941B-386E78BCC381}.Release|Any CPU.ActiveCfg = Release|Any CPU
1719
{95D249D8-C261-43E8-941B-386E78BCC381}.Release|Any CPU.Build.0 = Release|Any CPU
20+
{CD416881-1C3A-4A8A-84F4-F9FA2F884602}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{CD416881-1C3A-4A8A-84F4-F9FA2F884602}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{CD416881-1C3A-4A8A-84F4-F9FA2F884602}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{CD416881-1C3A-4A8A-84F4-F9FA2F884602}.Release|Any CPU.Build.0 = Release|Any CPU
1824
EndGlobalSection
1925
GlobalSection(SolutionProperties) = preSolution
2026
HideSolutionNode = FALSE

Requests/ProgressableContainer.cs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public ProgressableContainer() { }
2121
/// Constructor to merge <see cref="IRequest"/> together
2222
/// </summary>
2323
/// <param name="requests"><see cref="IRequest"/>s to merge</param>
24-
public ProgressableContainer(params TRequest[] requests) => Add(requests);
24+
public ProgressableContainer(params TRequest[] requests) => AddRange(requests);
2525

2626
/// <summary>
2727
/// Creates a new <see cref="ProgressableContainer{TRequest}"/> that megres <see cref="ProgressableContainer{TRequest}"/> together.
@@ -30,12 +30,9 @@ public ProgressableContainer() { }
3030
/// <returns>A new <see cref="ProgressableContainer{TRequest}"/></returns>
3131
public static ProgressableContainer<TRequest> MergeContainers(/*bool autoReset = false,*/ params ProgressableContainer<TRequest>[] requestContainers)
3232
{
33-
List<TRequest> requests = new();
34-
Array.ForEach(requestContainers, requestContainer => requests.AddRange(requestContainer.GetRequests()));
35-
return new ProgressableContainer<TRequest>(requests.ToArray())
36-
{
37-
// AutoReset = autoReset
38-
};
33+
ProgressableContainer<TRequest> container = new();
34+
Array.ForEach(requestContainers, requestContainer => container.AddRange(requestContainer.ToArray()));
35+
return container;
3936
}
4037

4138
/// <summary>
@@ -73,9 +70,9 @@ private void AttachProgress(TRequest request)
7370
/// Adds a range <see cref="IRequest"/> to the <see cref="ProgressableContainer{TRequest}"/>.
7471
/// </summary>
7572
/// <param name="requests">The <see cref="IRequest"/> to add.</param>
76-
public override void Add(params TRequest[] requests)
73+
public override void AddRange(params TRequest[] requests)
7774
{
78-
base.Add(requests);
75+
base.AddRange(requests);
7976
Array.ForEach(requests, req => AttachProgress(req));
8077
}
8178

Requests/RequestContainer.cs

Lines changed: 101 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
using Requests.Options;
2+
using System.Collections;
23

34
namespace Requests
45
{
56
/// <summary>
67
/// Class to manage and merge more than one TRequest.
78
/// </summary>
89
/// <typeparam name="TRequest">A RequestObject class</typeparam>
9-
public class RequestContainer<TRequest> : IRequest where TRequest : IRequest
10+
public class RequestContainer<TRequest> : IEnumerable<TRequest>, IRequest where TRequest : IRequest
1011
{
1112
private readonly List<TRequest> _requests = new();
1213
private bool _isrunning = true;
1314
private bool _isCanceled = false;
1415
private bool _disposed = false;
15-
private Task _task = Task.CompletedTask;
16+
private TaskCompletionSource? _task;
17+
private CancellationTokenSource _taskCancelationTokenSource = new();
1618
private RequestState _state = RequestState.Paused;
1719

1820
/// <summary>
1921
/// Merged task out the requests
2022
/// </summary>
21-
public Task Task => _task;
23+
public Task Task => _task?.Task ?? Task.CompletedTask;
2224

2325
/// <summary>
2426
/// State of this <see cref="RequestContainer{TRequest}"/>
@@ -28,6 +30,8 @@ public RequestState State
2830
get { return _state; }
2931
protected set
3032
{
33+
if (_state == value)
34+
return;
3135
_state = value;
3236
SynchronizationContext.Post((o) => StateChanged?.Invoke((IRequest)o!, value), this);
3337
}
@@ -44,7 +48,12 @@ protected set
4448
public RequestPriority Priority => RequestPriority.Normal;
4549

4650
/// <summary>
47-
/// The synchronization context captured upon construction. This will never be null.
51+
/// Gets the number of <see cref="IRequest"/> conntained in the <see cref="RequestContainer{TRequest}"/>
52+
/// </summary>
53+
public int Count => _requests.Count;
54+
55+
/// <summary>
56+
/// The synchronization context captured upon construction. This will never be null.
4857
/// </summary>
4958
protected SynchronizationContext SynchronizationContext { get; }
5059

@@ -57,13 +66,17 @@ protected set
5766
/// Constructor to merge <see cref="IRequest"/> together
5867
/// </summary>
5968
/// <param name="requests"><see cref="IRequest"/>s to merge</param>
60-
public RequestContainer(params TRequest[] requests) : this() => Add(requests);
69+
public RequestContainer(params TRequest[] requests) : this() => AddRange(requests);
6170

6271
/// <summary>
6372
/// Get all <see cref="IRequest"/> in this Container
6473
/// </summary>
6574
/// <returns>returns a <see cref="IRequest"/> array</returns>
66-
public IReadOnlyList<TRequest> GetRequests() => _requests;
75+
public TRequest this[int key]
76+
{
77+
get => _requests[key];
78+
set => _requests[key] = value;
79+
}
6780

6881
/// <summary>
6982
/// Creates a new <see cref="RequestContainer{TRequest}"/> that megres <see cref="RequestContainer{TRequest}"/> together.
@@ -72,17 +85,16 @@ protected set
7285
/// <returns></returns>
7386
public static RequestContainer<TRequest> MergeContainers(params RequestContainer<TRequest>[] requestContainers)
7487
{
75-
List<TRequest> requests = new();
76-
Array.ForEach(requestContainers, requestContainer => requests.AddRange(requestContainer._requests));
77-
return new RequestContainer<TRequest>(requests.ToArray());
88+
RequestContainer<TRequest> newContainer = new();
89+
Array.ForEach(requestContainers, requestContainer => newContainer.AddRange(requestContainer.ToArray()));
90+
return newContainer;
7891
}
7992

8093
/// <summary>
8194
/// Main Contructor for <see cref="RequestContainer{TRequest}"/>.
8295
/// </summary>
8396
public RequestContainer() => SynchronizationContext = SynchronizationContext.Current ?? new();
8497

85-
8698
/// <summary>
8799
/// Adds a <see cref="IRequest"/> to the <see cref="RequestContainer{TRequest}"/>.
88100
/// </summary>
@@ -98,56 +110,80 @@ public virtual void Add(TRequest request)
98110

99111
request.StateChanged += OnStateChanged;
100112
_requests.Add(request);
101-
_task = Task.WhenAll(_requests.Select(request => request.Task));
102-
}
103-
104-
private void OnStateChanged(object? sender, RequestState state)
105-
{
106-
if (state == State)
107-
return;
108-
if (state != RequestState.Failed)
109-
{
110-
int[] states = _requests.Select(req => (int)req.State).ToArray();
111-
int[] counter = new int[7];
112-
foreach (int value in states)
113-
counter[value]++;
114-
115-
if (counter[(int)RequestState.Running] > 1)
116-
state = RequestState.Running;
117-
else if (counter[(int)RequestState.Idle] > 1)
118-
state = RequestState.Idle;
119-
else
120-
{
121-
int max = counter.Max();
122-
state = (RequestState)counter.ToList().IndexOf(max);
123-
}
124-
}
125-
if (state != State)
126-
State = state;
113+
NewTaskCompletion();
114+
OnStateChanged(this, request.State);
127115
}
128116

129-
async Task IRequest.StartRequestAsync()
117+
private void NewTaskCompletion()
130118
{
131-
_isrunning = true;
132-
foreach (TRequest? request in _requests)
133-
await request.StartRequestAsync();
119+
_taskCancelationTokenSource.Cancel();
120+
_taskCancelationTokenSource = new();
121+
if (Task.IsCompleted)
122+
_task = new(TaskCreationOptions.RunContinuationsAsynchronously);
123+
Task.WhenAll(_requests.Select(request => request.Task)).ContinueWith(task => _task?.TrySetResult(), _taskCancelationTokenSource.Token);
134124
}
135125

136126
/// <summary>
137127
/// Adds a range <see cref="IRequest"/> to the <see cref="RequestContainer{TRequest}"/>.
138128
/// </summary>
139129
/// <param name="requests">The <see cref="IRequest"/> to add.</param>
140-
public virtual void Add(params TRequest[] requests)
130+
public virtual void AddRange(params TRequest[] requests)
141131
{
142132
if (_isCanceled)
143133
Array.ForEach(requests, request => request.Cancel());
144134
else if (_disposed)
145135
Array.ForEach(requests, request => request.Dispose());
146136
else if (!_isrunning)
147137
Array.ForEach(requests, request => request.Pause());
148-
138+
Array.ForEach(requests, request => request.StateChanged += OnStateChanged);
149139
_requests.AddRange(requests);
150-
_task = Task.WhenAll(_requests.Select(request => request.Task));
140+
NewTaskCompletion();
141+
State = CalculateState();
142+
}
143+
144+
145+
private void OnStateChanged(object? sender, RequestState state)
146+
{
147+
if (state == State)
148+
return;
149+
if (state != RequestState.Failed)
150+
state = CalculateState();
151+
State = state;
152+
}
153+
154+
private RequestState CalculateState()
155+
{
156+
RequestState state;
157+
IEnumerable<int> states = _requests.Select(req => (int)req.State);
158+
int[] counter = new int[7];
159+
foreach (int value in states)
160+
counter[value]++;
161+
162+
if (counter[6] > 0)
163+
state = RequestState.Failed;
164+
else if (counter[1] > 0)
165+
state = RequestState.Running;
166+
else if (counter[5] > 0)
167+
state = RequestState.Cancelled;
168+
else if (counter[0] > 0)
169+
state = RequestState.Idle;
170+
else if (counter[4] > 0)
171+
state = RequestState.Waiting;
172+
else if (counter[2] == _requests.Count)
173+
state = RequestState.Compleated;
174+
else if (counter[3] > 0)
175+
state = RequestState.Paused;
176+
else
177+
state = (RequestState)Array.IndexOf(counter, counter.Max());
178+
179+
return state;
180+
}
181+
182+
async Task IRequest.StartRequestAsync()
183+
{
184+
_isrunning = true;
185+
foreach (TRequest? request in _requests)
186+
await request.StartRequestAsync();
151187
}
152188

153189
/// <summary>
@@ -156,10 +192,18 @@ public virtual void Add(params TRequest[] requests)
156192
/// <param name="requests">Request to remove</param>
157193
public virtual void Remove(params TRequest[] requests)
158194
{
159-
Array.ForEach(requests, request => _requests.Remove(request));
160-
if (_requests.Count > 0)
161-
_task = Task.WhenAll(_requests.Select(request => request.Task));
162-
else _task = Task.CompletedTask;
195+
Array.ForEach(requests, request =>
196+
{
197+
_requests.Remove(request);
198+
request.StateChanged -= StateChanged;
199+
});
200+
if (_requests.Count > 0 && !Task.IsCompleted)
201+
NewTaskCompletion();
202+
else
203+
_task = null;
204+
if (State is not RequestState.Compleated and not RequestState.Paused)
205+
State = CalculateState();
206+
163207
}
164208

165209
/// <summary>
@@ -180,6 +224,7 @@ public void Start()
180224
foreach (TRequest? request in _requests)
181225
request.Start();
182226
}
227+
183228
/// <summary>
184229
/// Put every <see cref="IRequest"/> in Container on hold
185230
/// </summary>
@@ -203,5 +248,14 @@ public void Dispose()
203248
request.Dispose();
204249
GC.SuppressFinalize(this);
205250
}
251+
252+
/// <summary>
253+
/// Returns an enumerator that iterates through the <see cref="RequestContainer{TRequest}"/>
254+
/// </summary>
255+
/// <returns> A <see cref="RequestContainer{TRequest}"/> .Enumerator for the <see cref="RequestContainer{TRequest}"/> .</returns>
256+
public IEnumerator<TRequest> GetEnumerator() => _requests.GetEnumerator();
257+
258+
/// <inheritdoc/>
259+
IEnumerator IEnumerable.GetEnumerator() => _requests.GetEnumerator();
206260
}
207261
}

Requests/Requests.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
<PackageTags>async; channel; priority; request; parallel; </PackageTags>
1717
<RepositoryUrl>https://github.com/TypNull/Requests</RepositoryUrl>
1818
<PackageIcon>logo.png</PackageIcon>
19-
<Version>2.0.1</Version>
19+
<Version>2.0.2</Version>
2020
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
2121
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
2222
<PackageId>Shard.Requests</PackageId>
23-
<PackageReleaseNotes>
24-
Minor RequestContainer improvements
25-
</PackageReleaseNotes>
23+
<PackageReleaseNotes>Fix RequestContainer Task issue,
24+
Fix RequestContainer State issue
25+
Add IEnumerable to RequestContainer</PackageReleaseNotes>
2626
</PropertyGroup>
2727

2828
<ItemGroup>

0 commit comments

Comments
 (0)