What is the purpose of passing CancellationToken to Task.Factory.StartNew() - task-parallel-library

In the following code a CancellationToken is passed to the .StartNew(,) method as the 2nd parameter, but is only usable by the Action via the closure in the lambda. So, what is the purpose of passing the token through the .StartNew(,) method's 2nd parameter?
var cts = new CancellationTokenSource();
var token = cts.Token;
Task.Factory.StartNew(() =>
{
while (true)
{
// simulate doing something useful
Thread.Sleep(100);
}
}, token);

StartNew method schedules a task in the tread pool, but not necessary start it right at the moment, because threads may be unavailable. During waiting for the start the cancellation request may occur, after which the thread pool wouldn't start a task at all. After task was started it's your job to handle cancellation of the task.

Actually, the purpose of the CancellationToken passed to Task.Run and Taskfactory.StartNew is to allow the task to differentiate being cancelled by an exception thrown from CancellationToken.ThrowIfCancellationRequested and failing because of any other exception.
That is, if the CancellationToken passed at start throws, the task's state is Cancelled while any other exception (even from another CancellationToken) will set it to Faulted.
Also, if the CancellationToken is cancelled before the task actually starts, it won't be started at all.

Related

Weird Behavior while missing an await keyword generates error: Cannot access a disposed context instance

I recently ran into this error. I have never came across this before so wondering!
Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'OrderDbContext'.
The only thing i missed which produced this error is the await keyword in the controller action method before _mediator.Send(checkoutCommand); Once I added the await keyword this error vanished.
What (the heck) is wrong with missing this await keyword? The error does not explicitly state that. Can someone explain why missing an await keyword cause an error that database context is disposed?
Controller Action:
public async Task<IActionResult> Checkout(CheckoutOrderCommand checkoutCommand)
{
**var id = _mediator.Send(checkoutCommand);**
return CreatedAtRoute("Get Order by Id", id);
}
CQRS Mediatr Instructions
public class CheckoutOrderCommandHandler : IRequestHandler<CheckoutOrderCommand, int>
{
private readonly IOrderUow _orderUow;
private readonly IMapper _mapper;
public CheckoutOrderCommandHandler(IOrderUow orderUow,
IMapper mapper)
{
_orderUow = orderUow;
_mapper = mapper;
}
public async Task<int> Handle(CheckoutOrderCommand request, CancellationToken cancellationToken)
{
var order = _mapper.Map<Order>(request);
// Populate base entity fields
order.CreatedBy = Constants.CurrentUser;
// create new order
var newOrder = await _orderUow.AddOrderAsync(order);
return newOrder.Id;
}
}
Unit of work implementation
public class OrderUow : IOrderUow
{
private readonly OrderDbContext _orderContext;
public OrderUow(OrderDbContext orderContext)
{
_orderContext = orderContext;
}
public async Task<Order> AddOrderAsync(Order order)
{
try
{
await _orderContext.Orders.AddAsync(order);
await _orderContext.SaveChangesAsync();
}
catch (Exception ex)
{
}
return order;
}
}
Missing an await without explicitly handling the task that is returned will mean that the code calling the asynchronous method will not create a resumption point and instead will continue executing to completion, in your case leading to the disposal of the DbContext.
Asyncrhronous code is multi-threaded behind the scenes. Think of it this way, your web request enters on Thread #1 which creates a DbContext instance via an IoC container, calls an asynchronous method, then returns. When the code calls an async method, it automatically hands that code off to a worker thread to execute. By adding await you tell .Net to create a resume point to come back to. That may be the original calling thread, or a new worker thread, though the important thing is that the calling code will resume only after the async method completes. Without await, there is no resume point, so the calling code continues after the async method call. This can lead to all kinds of bad behaviour. The calling code can end up completing and disposing the DbContext (what you are seeing) or if it calls another operation against the DbContext you could end up with an exception complaining about access across multiple threads since a DbContext detects that and does not allow access across threads.
You can observe the threading behaviour by inspecting Thread.CurrentThread.ManagedThreadId before the async method call, inside the async method, then after the async method call.
Without the await you would see Thread #X before the method call, then Thread #Y inside the async method, then back to Thread #X after. While debugging it would most likely appear to work since the worker thread would likely finish by the time you were done with the breakpoints, but at runtime that worker thread (Y) would have been started, but the code after the call on thread X would continue running, ultimately disposing of your DbContext likely before Thread Y was finished executing. With the await call, you would likely see Thread #X before the method call, Thread #Y inside, then Thread #Z after the call. The breakpoint after the async call would only be triggered after the async method completes. Thread #X would be released while waiting for the async method, but the DbContext etc. wouldn't be disposed until the resumption point created by awaiting the async method had run. It is possible that the code can resume on the original thread (X) if that thread is available in the pool.
This is a very good reason to follow the general naming convention for asynchronous methods to use the "Async" suffix for the method name. If your Mediator.Send method is async, consider naming it "SendAsync" to make missing await statements a lot more obvious. Also check those compiler warnings! If your project has a lot of warnings that are being ignored, this is a good reason to go through and clean them up so new warnings like this can be spotted quickly and fixed. This is one of the first things I do when starting with a new client is look at how many warnings the team has been ignoring and pointing out some of the nasty ones they weren't aware were lurking in the code base hidden by the noise.

How to resolve "Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Reading the request body timed out due to data arriving too slowly"?

While doing API Performance Testing [e.g. No of Threads/Users : 50 with Loop Count : 10], 5 to 8 % samples with POST methods are failing due to below exception :
**Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Reading the request body timed out due to data arriving too slowly. See MinRequestBodyDataRate.**
at Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException.Throw(RequestRejectionReason reason)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.PumpAsync()
at System.IO.Pipelines.PipeCompletion.ThrowLatchedException()
at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
at System.IO.Pipelines.Pipe.GetReadAsyncResult()
at System.IO.Pipelines.Pipe.DefaultPipeReader.GetResult(Int16 token)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.MessageBody.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.ReadAsyncInternal(Memory`1 buffer, CancellationToken cancellationToken)
at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
at Microsoft.AspNetCore.WebUtilities.StreamHelperExtensions.DrainAsync(Stream stream, ArrayPool`1 bytePool, Nullable`1 limit, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Mvc.Formatters.JsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder.BindModelAsync(ModelBindingContext bindingContext)
at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value)
Could you please suggest how i can fix this ?
NOTE :I correctly implemented async/await in POST method.This is
happening to POST method only and out of 500 samples only 20 to
40 samples are failing per POST method. Providing same inputs while
testing.
You could setKestrelServerLimits.MinRequestBodyDataRate property to null in your Program.cs as below:
.UseKestrel(opt =>
{
opt.Limits.MinRequestBodyDataRate = null;
});
Reference:
KestrelServerLimits.MinRequestBodyDataRate Property
Requests should be resolved fast, and this is a clear warning, that they are not. Exception is, that we are debugging/single-stepping the service, in which case I would prefer more tolerant request limits. In live environments, DDoS and even heavy load can render a system or service unstable very quickly, if limits are too high, because a growing request stack can overwhelm the system.
I made a cache service built on Kestrel and I found two scenarios which could cause this error:
The service received a massive amounts of items for a write operation on a quite busy DB table. This happened due to my own poor design. To resolve: either to optimize the update flow or create a per-task based update handler.
Service requests have stalled due to an instability, suspension or shut-down. (f.ex. TCP connections may not always shut down immediately using a static HttpClient, it sometimes fails to exit gracefully on shut-down)
Code below is from a NET 7.0 based kestrel service
// Limits can also be applied within appsettings.json,
appBuilder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.Limits.MaxConcurrentConnections = 100;
serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
#if !DEBUG
// Live: Demand higher request data-rate limits and low grace period, services can be unreliable.
// May cause requests to stall resulting in BadHttpRequestException.
// Catch them in your error handler
serverOptions.Limits.MinRequestBodyDataRate = new MinDataRate(
bytesPerSecond: 100,
gracePeriod: TimeSpan.FromSeconds(5)
);
#endif
});
Add error handler to app
// global error handler
app.UseMiddleware<ErrorHandlerMiddleware>();
Error handler:
public class ErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
#if !DEBUG
catch (Microsoft.AspNetCore.Connections.ConnectionResetException)
{
// Not of interest - client has ended the connection prematurely. Happens all the time.
return;
}
#endif
catch (BadHttpRequestException bad) when (bad.Message.Contains("data arriving too slowly"))
{
#if DEBUG
// The original exception lacks insight.
throw new BadHttpRequestException($"Request data arriving too slowly: {context.Request.Path}");
#endif
}
}
}

ReactiveUI: Should I expect to receive a cancellationToken.IsCancellationRequested using TakeUntil()

I have two simple Run/Cancel commands
Run = ReactiveCommand.CreateAsyncTask(x => RunImpl());
Run.ThrownExceptions.Subscribe(ex => UserError.Throw(ex.Message, ex));
Cancel = this.WhenAnyObservable(x => x.Run.IsExecuting).ToCommand();
Run.TakeUntil(Cancel);
Should I expect a cancellation token signaled into my async method when Cancel is executed?
private async Task<bool> RunImpl(CancellationToken cancellationToken = default(CancellationToken))
{
//some stuffs
if (cancellationToken.IsCancellationRequested)
return;
//next stuffs
}
If it´s so, what I'm doing wrong? Because IsCancellationRequest never goes true.
Thanks in advance.
Run.TakeUntil(Cancel);
This has no effect, as it only creates a new observable, which will never be used.
Run = ReactiveCommand.CreateAsyncTask(x => RunImpl());
This will provide a default (never used) cancellationToken, hence will never cancel.
I believe the correct way is:
Run = ReactiveCommand.CreateAsyncObservable(_ => Observable.StartAsync(RunImpl).TakeUntil(Cancel));
Cancel = this.WhenAnyObservable(x => x.Run.IsExecuting).ToCommand();
This way, the observable returned by TakeUntil will be suscribed by the command when executing, and TakeUntil will take care of canceling whenever the Cancel command runs.

Handling configuration change for async methods

I have an activity which has an async method in it. This async method is long running. After the async method returns, the UI needs to be updated and some of the controls reference the activity.
At the moment, everything works correctly if you do not have a configuration change (like screen rotation) while the async task is running. However, if a configuration change happens while it is running, then the exception Activity is destroyed is thrown and the UI is not updated. From what reading I have done, this seems to be because the async method captures context and then tries to update the old context which is of course destroyed after the configuration change.
My question is: What are the best ways to solve this problem or at worst case scenario work around it?
I personally think you have only three options
You can disable rotation permanently or temporary, but this is a bad practice
To disable it permanently set ConfigurationChanges
[Activity(Label = "...", ConfigurationChanges = Android.Content.PM.ConfigChanges.KeyboardHidden | Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize)]
To disable it temporary while task working you should disable rotation handling,
disable
this.RequestedOrientation = Android.Content.PM.ScreenOrientation.Nosensor;
enable
this.RequestedOrientation = Android.Content.PM.ScreenOrientation.Sensor;
If you are using fragment you can prevent fragment destroy with RetainInstance = true. That might work, but i never tested it.
You can cancel task with CancelationToken and restart it in OnRestoreInstanceState()
Here is example how to cancel task
{
CancellationTokenSource cts;
...
// If a download process is already underway, cancel it.
if (cts != null)
{
cts.Cancel();
}
// Now set cts to cancel the current process if the button is chosen again.
CancellationTokenSource newCTS = new CancellationTokenSource();
cts = newCTS;
try
{
//Send cts.Token to carry the message if there is a cancellation request.
await AccessTheWebAsync(cts.Token);
}
// Catch cancellations separately.
catch (OperationCanceledException)
{
ResultsTextBox.Text += "\r\nDownloads canceled.\r\n";
}
catch (Exception)
{
ResultsTextBox.Text += "\r\nDownloads failed.\r\n";
}
// When the process is complete, signal that another process can proceed.
if (cts == newCTS)
cts = null;
}
And in the task
async Task AccessTheWebAsync(CancellationToken ct)
{
...
// Retrieve the website contents from the HttpResponseMessage.
byte[] urlContents = await response.Content.ReadAsByteArrayAsync();
// Check for cancellations before displaying information about the
// latest site.
ct.ThrowIfCancellationRequested();
...
}
There are plenty of things you could do, but please don't go and disable the phones ability to turn the screen -- that is just going to ignore your users.
At a highlevel you will have to do two things:
Make sure the async task keeps running and is not restarted if the activity dies.
You can solve that by moving the task either into the application class or (cleaner) into a headless fragment with setRetainInstance set to true.
In the onDestroy method in the activity, remove it from the async task, in the onCreate task give the activity to the async task (if it exist).
This is what prevents the async task from calling the old context and can be done with a simple java setter on the async task. Don't forget to cache the result in the task if the activity is currently not connected.
In the end what I ended up doing was encapsulating the async task in another class which held a reference to the current activity, which implemented and interface which defined a method which handles the async response and updates the UI.
The activity held a static variable of the encapsulated async task, and if it was running during a config change, the encapsulated async's task reference to the activity was updated to the new activity.

How is async network I/O handled with a WP7 Scheduled Task?

With Mango, it's possible to create a scheduled task to update the ShellTiles data.
It's up to the task to call NotifyComplete() when it's done.
Given that I/O on the phone should be asynchronous, how do you ensure that your I/O is complete prior to calling NotifyComplete()?
Via synchronization primatives? Or will the I/O be allowed to complete once the Task has notified the phone's OS it's complete?
Sync primatives is the obvious answer, but on the phone, blocking isn't really a good choice.
Scheduled tasks are not executed synchronously. They are started and then have 15 seconds to call NotifyComplete (or abort) before they are forcefully terminated.
In direct answer to your question, you would use the asynchronous IO methods and then call NotifyComplete from the complete event or callback.
Here's an example. I've used the Microsoft.Phone.Reactive stuff but you can use Begin/EndGetResponse in the traditional way if you prefer.
public class SampleTask : ScheduledTaskAgent
{
protected override void OnInvoke(ScheduledTask task)
{
HttpWebRequest request = WebRequest.CreateHttp("http://stackoverflow.com");
Observable.FromAsyncPattern<WebResponse>(
request.BeginEndResponse,
request.EndGetResponse
)()
.Subscribe(response =>
{
// Process the response
NotifyComplete();
}, ex =>
{
// Process the error
Abort(); // Unschedules the task (if the exception indicates
// the task cannot run successfully again)
});
// Synchronous control flow will continue and exit the OnInvoke method
}
}

Resources