Skip to content

Commit 01d5c35

Browse files
author
Meyn
committed
RequestContainer minor improvements
1 parent 8a3a921 commit 01d5c35

File tree

3 files changed

+72
-33
lines changed

3 files changed

+72
-33
lines changed

Requests/RequestContainer.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,17 @@ public virtual void Remove(params TRequest[] requests)
274274

275275
while (Interlocked.CompareExchange(ref _writeInProgress, 1, 0) == 1)
276276
Thread.Yield();
277-
_requests = GetStored().Where(x => !requests.Any(y => y.Equals(x))).ToArray();
277+
278+
var storedRequests = GetStored().Where(x => !requests.Any(y => y.Equals(x))).ToArray();
279+
280+
int newSize = storedRequests.Length + 32;
281+
282+
Array.Resize(ref storedRequests, newSize);
283+
284+
_requests = storedRequests;
278285
_count = _requests.Length;
279286

280-
Interlocked.Exchange(ref _writeInProgress, 0); // Release the write lock
287+
Interlocked.Exchange(ref _writeInProgress, 0);
281288

282289
if (_count > 0 && !Task.IsCompleted)
283290
NewTaskCompletion();

Requests/RequestHandler.cs

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Requests.Channel;
22
using Requests.Options;
3+
using System.Text;
34

45
namespace Requests
56
{
@@ -45,8 +46,8 @@ protected set
4546
/// <summary>
4647
/// Represents the combined task of the requests.
4748
/// </summary>
48-
public Task Task => _task.Task;
49-
private readonly TaskCompletionSource _task = new(TaskCreationOptions.RunContinuationsAsynchronously);
49+
public Task Task => _task ?? Task.CompletedTask;
50+
private Task? _task;
5051

5152
/// <summary>
5253
/// Gets the aggregate exception associated with the <see cref="RequestHandler"/> instance.
@@ -102,6 +103,24 @@ public int? StaticDegreeOfParallelism
102103
/// </summary>
103104
public int Count => _requestsChannel.Count;
104105

106+
/// <summary>
107+
/// Initializes a new instance of the <see cref="RequestHandler"/> class with a priority channel.
108+
/// If the priority count is zero, an exception is thrown; otherwise, a fixed-size priority channel is created.
109+
/// If the priority count is null, a dynamic-size priority channel is created.
110+
/// </summary>
111+
/// <param name="priorityCount">The number of priority levels for the fixed-size priority channel. If zero, an exception is thrown. If null, a dynamic-size priority channel is used.</param>
112+
/// <exception cref="ArgumentOutOfRangeException">Thrown if the priority count is negative or zero.</exception>
113+
public RequestHandler(int priorityCount = 3)
114+
{
115+
if (priorityCount < 0)
116+
throw new ArgumentOutOfRangeException(nameof(priorityCount), "Priority count cannot be negative.");
117+
118+
_requestsChannel = priorityCount > 0 ?
119+
new FixedPriorityChannel<IRequest>(priorityCount)
120+
: new DynamicPriorityChannel<IRequest>();
121+
}
122+
123+
105124
/// <summary>
106125
/// Constructor for the <see cref="RequestHandler"/> class.
107126
/// </summary>
@@ -113,20 +132,6 @@ public RequestHandler(params IRequest[] requests) : this()
113132
_requestsChannel.Options.MaxDegreeOfParallelism = Math.Min(AutoParallelism.Invoke(), MaxParallelism);
114133
}
115134

116-
/// <summary>
117-
/// Initializes a new instance of the <see cref="RequestHandler"/> class with a priority channel.
118-
/// If the priority count is zero, a dynamic-size priority channel is created; otherwise, a fixed-size priority channel is created.
119-
/// </summary>
120-
/// <param name="priorityCount">The number of priority levels for the fixed-size priority channel. If zero, a dynamic-size priority channel is used.</param>
121-
/// <exception cref="ArgumentOutOfRangeException">Thrown if the priority count is negative.</exception>
122-
public RequestHandler(int priorityCount = 3)
123-
{
124-
if (priorityCount < 0)
125-
throw new ArgumentOutOfRangeException(nameof(priorityCount), "Priority count cannot be negative.");
126-
127-
_requestsChannel = priorityCount == 0 ? new DynamicPriorityChannel<IRequest>() : new FixedPriorityChannel<IRequest>(priorityCount);
128-
}
129-
130135
/// <summary>
131136
/// Method to add a single instance of the <see cref="IRequest"/> interface to the handler.
132137
/// </summary>
@@ -211,7 +216,11 @@ public void CreateCTS()
211216
/// <summary>
212217
/// Cancels the main <see cref="CancellationTokenSource"/> for all instances of the <see cref="IRequest"/> interface in this RequestHandler.
213218
/// </summary>
214-
public void Cancel() => _cts.Cancel();
219+
public void Cancel()
220+
{
221+
_cts.Cancel();
222+
State = RequestState.Cancelled;
223+
}
215224

216225
/// <summary>
217226
/// Cancels the main <see cref="CancellationTokenSource"/> for all instances of the <see cref="IRequest"/> interface in the Main RequestHandlers.
@@ -228,16 +237,15 @@ public void CreateCTS()
228237
/// </summary>
229238
public static void ReusmeMain() => Array.ForEach(MainRequestHandlers, handler => handler.Start());
230239

231-
232240
/// <summary>
233241
/// This method is responsible for executing the instances of the <see cref="IRequest"/> if the handler is not currently running.
234242
/// It updates the degree of parallelism based on the current system environment and runs the request channel.
235243
/// </summary>
236244
public void RunRequests()
237245
{
238-
if (State == RequestState.Running || CancellationToken.IsCancellationRequested || _pts.IsPaused)
246+
if (State != RequestState.Idle)
239247
return;
240-
Task.Run(async () => await RunChannel());
248+
Task.Run(async () => await ((IRequest)this).StartRequestAsync());
241249
}
242250

243251
/// <summary>
@@ -246,9 +254,10 @@ public void RunRequests()
246254
/// </summary>
247255
async Task IRequest.StartRequestAsync()
248256
{
249-
if (State == RequestState.Running || CancellationToken.IsCancellationRequested || _pts.IsPaused)
257+
if (State != RequestState.Idle || CancellationToken.IsCancellationRequested || _pts.IsPaused)
250258
return;
251-
await RunChannel();
259+
_task = RunChannel();
260+
await Task;
252261
}
253262

254263
/// <summary>
@@ -261,8 +270,8 @@ private async Task RunChannel()
261270
UpdateAutoParallelism();
262271
await _requestsChannel.RunParallelReader(async (pair, ct) => await HandleRequests(pair));
263272
State = RequestState.Idle;
264-
if (_requestsChannel.Reader.Count != 0)
265-
RunRequests();
273+
if (_requestsChannel.Reader.Count > 0)
274+
await ((IRequest)this).StartRequestAsync();
266275
}
267276

268277
/// <summary>
@@ -299,7 +308,6 @@ public void UpdateAutoParallelism()
299308
/// <summary>
300309
/// Attempts to set all <see cref="IRequest"/> objects in the container's <see cref="State"/> to idle.
301310
/// No new requests will be started or read while processing. And the running requests will be paused.
302-
/// After finishing, the request handler will still be paused.
303311
/// </summary>
304312
/// <returns>True if all <see cref="IRequest"/> objects are in an idle <see cref="RequestState"/>, otherwise false.</returns>
305313
public bool TrySetIdle()
@@ -319,13 +327,36 @@ public void Dispose()
319327
if (_disposed)
320328
return;
321329

322-
_task.SetResult();
323330
Cancel();
324331
_cts.Dispose();
325332
_disposed = true;
326333
GC.SuppressFinalize(this);
327334
}
328335

336+
/// <summary>
337+
/// Provides a detailed string representation of the current state of the <see cref="RequestHandler"/> instance.
338+
/// </summary>
339+
/// <returns>A string that represents the current state of the <see cref="RequestHandler"/>.</returns>
340+
public override string ToString()
341+
{
342+
var sb = new StringBuilder();
343+
344+
sb.AppendLine("RequestHandler State:");
345+
sb.AppendLine($" Disposed: {_disposed}");
346+
sb.AppendLine($" Cancellation Requested: {_cts.IsCancellationRequested}");
347+
sb.AppendLine($" Paused: {_pts.IsPaused}");
348+
sb.AppendLine($" State: {State}");
349+
sb.AppendLine($" Priority: {Priority}");
350+
sb.AppendLine($" Task Status: {Task.Status}");
351+
sb.AppendLine($" Exception: {Exception?.Message ?? "None"}");
352+
sb.AppendLine($" Static Degree of Parallelism: {StaticDegreeOfParallelism?.ToString() ?? "Auto"}");
353+
sb.AppendLine($" Max Parallelism: {MaxParallelism}");
354+
sb.AppendLine($" Request Count: {Count}");
355+
sb.AppendLine($" CancellationToken: {CancellationToken.IsCancellationRequested}");
356+
sb.AppendLine($" PauseToken: {_pts.IsPaused}");
357+
358+
return sb.ToString();
359+
}
329360
/// <summary>
330361
/// Attempts to remove the specified requests from the priority channel.
331362
/// </summary>

Requests/Requests.csproj

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,22 @@
88
<GenerateDocumentationFile>True</GenerateDocumentationFile>
99
<Authors>Meyn</Authors>
1010
<Company>Shard</Company>
11-
<Description>Requests is your go-to C# .NET 6 library for handling requests in a parallel async state. It’s like having a reliable co-pilot for your code. With features like priority settings, retry functions, and expandable classes, it’s the perfect companion for HTTP requests and other CPU-intensive tasks. Features include retry functions, priority settings, and notification delegates.</Description>
11+
<Description>A C# .NET 6 library for parallel async request handling. Features include priority settings, retry functions, and expandable classes, making it ideal for HTTP requests and CPU-intensive tasks.</Description>
1212
<Copyright>Shard © 2024</Copyright>
1313
<PackageProjectUrl></PackageProjectUrl>
1414
<PackageReadmeFile>README.md</PackageReadmeFile>
1515
<RepositoryType>git</RepositoryType>
1616
<PackageTags>async; channel; priority; request; parallel; </PackageTags>
1717
<RepositoryUrl>https://github.com/TypNull/Requests</RepositoryUrl>
1818
<PackageIcon>logo.png</PackageIcon>
19-
<Version>2.1.5</Version>
19+
<Version>2.1.6</Version>
2020
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
2121
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
2222
<PackageId>Shard.Requests</PackageId>
23-
<PackageReleaseNotes>Fix CTS cancellation issue
24-
Enhance RequestContainer concurrency
25-
Expand unit test coverage</PackageReleaseNotes>
23+
<PackageReleaseNotes>Introduced DynamicPriorityChannel to manage dynamic priority handling.
24+
Implemented RequestContainer interface into RequestHandler to add request logic.
25+
Changed RequestPriority to a floating type for enhanced flexibility.
26+
RequestHandler minor improvements</PackageReleaseNotes>
2627
</PropertyGroup>
2728

2829
<ItemGroup>

0 commit comments

Comments
 (0)