Subscribe to single item with defined "retries" and "timeout" - c#

I have an IObservable<Packet> which is a hot observable, that allows different subscriptions to analyze incoming packets.
I want to write a method, that sends some data with and ID, and waits for response with the same ID. Pseudocode:
void SendData(byte[] data, int retries, int timeout, Action<Packet> success, Action fail)
{
var sequenceId = GetSequenceId();
_port.SendData(data, sequenceId);
_packetStream.Where(p => p.SequenceId == sequenceId)
.Take(1)
.WaitForTimeout(timeout)
.WaitForRetry(retries)
.Subscribe(success) //Need to unsubscribe after packet is received
//If we didn't receive an answer packet, then call fail() action
}
Don't really know, how this stuff is usually done with Reactive Extensions. Would be really glad to receive some suggestions. Thanks.

The code in your question looks close to right. The two "wait for" methods exist in the Rx framework (Timeout and Retry). I would recommend you change your method to return an IObservable and drop the success and fail parameter. Doing so "keeps you in the monad" and lets you chain further operators onto the observable if needed. The success and fail parameters are instead used when you subscribe to the resulting observable (as OnNext and OnError respectively).
I assume the data should be resent on a timeout (otherwise you are not really retrying). To do that, you can use Observable.Create to send the data upon subscription.
IObservable<Packet> SendData(byte[] data, int retries, TimeSpan timeout)
{
//only get the sequence id once per call to SendData, regardless of retries
var sequenceId = GetSequenceId();
return Observable.Create(obs =>
{ //this code runs every time you subscribe
_port.SendData(data, sequenceId);
return _packetStream.Where(p => p.SequenceId == sequenceId)
.Take(1)
.Timeout(timeout)
.Subscribe(obs)
})
.Retry(retries);
}
Putting the Retry operator at the end causes, the Create observable to be retried if it times out. As an aside, there are overloads of Timeout that allow you to pass in another observable sequence to use in the case of timeout. You can use this overload along with Observable.Throw to provide your own exception in case of a timeout if desired, such as to provide an alternate error message.
Note that this code does not send data until you subscribe and does not block until the result is returned or the timeout is reached but does let you cancel further retries by Disposing the subscription. This code also does not prevent you from sending multiple packets at the same time. If you must block, you can do something like this:
var response = SendData(/* arguments */);
response.Do(success, fail).StartWith(null).ToTask().Wait();
If you are using C# 5 and calling this within an async method, you can await the observable.

What I got so far is this:
private void WaitForAnswer(byte sequenceId, int timeout, Action<Packet> success, Action fail, int retriesLeft)
{
_packetStream.Where(p => p.GetSequence() == sequenceId)
.Take(1)
.Timeout(TimeSpan.FromMilliseconds(timeout))
.Subscribe(success,
_ => {
if (retriesLeft > 0) WaitForAnswer(sequenceId, timeout, success, fail, --retriesLeft);
else fail();
},
() => { });
}
I'm not quite sure though, if this solution correctly disposes of subscription.

Related

Block Controller Method while already running

I have a controller which returns a large json object. If this object does not exist, it will generate and return it afterwards. The generation takes about 5 seconds, and if the client sent the request multiple times, the object gets generated with x-times the children. So my question is: Is there a way to block the second request, until the first one finished, independent who sent the request?
Normally I would do it with a Singleton, but because I am having scoped services, singleton does not work here
Warning: this is very oppinionated and maybe not suitable for Stack Overflow, but here it is anyway
Although I'll provide no code... when things take a while to generate, you don't usually spend that time directly in controller code, but do something like "start a background task to generate the result, and provide a "task id", which can be queried on another different call).
So, my preferred course of action for this would be having two different controller actions:
Generate, which creates the background job, assigns it some id, and returns the id
GetResult, to which you pass the task id, and returns either different error codes for "job id doesn't exist", "job id isn't finished", or a 200 with the result.
This way, your clients will need to call both, however, in Generate, you can check if the job is already being created and return an existing job id.
This of course moves the need to "retry and check" to your client: in exchange, you don't leave the connection to the server opened during those 5 seconds (which could potentially be multiplied by a number of clients) and return fast.
Otherwise, if you don't care about having your clients wait for a response during those 5 seconds, you could do a simple:
if(resultDoesntExist) {
resultDoesntExist = false; // You can use locks for the boolean setters or Interlocked instead of just setting a member
resultIsBeingGenerated = true;
generateResult(); // <-- this is what takes 5 seconds
resultIsBeingGenerated = false;
}
while(resultIsBeingGenerated) { await Task.Delay(10); } // <-- other clients will wait here
var result = getResult(); // <-- this should be fast once the result is already created
return result;
note: those booleans and the actual loop could be on the controller, or on the service, or wherever you see fit: just be wary of making them thread-safe in however method you see appropriate
So you basically make other clients wait till the first one generates the result, with "almost" no CPU load on the server... however with a connection open and a thread from the threadpool used, so I just DO NOT recommend this :-)
PS: #Leaky solution above is also good, but it also shifts the responsability to retry to the client, and if you are going to do that, I'd probably go directly with a "background job id", instead of having the first (the one that generates the result) one take 5 seconds. IMO, if it can be avoided, no API action should ever take 5 seconds to return :-)
Do you have an example for Interlocked.CompareExchange?
Sure. I'm definitely not the most knowledgeable person when it comes to multi-threading stuff, but this is quite simple (as you might know, Interlocked has no support for bool, so it's customary to represent it with an integral type):
public class QueryStatus
{
private static int _flag;
// Returns false if the query has already started.
public bool TrySetStarted()
=> Interlocked.CompareExchange(ref _flag, 1, 0) == 0;
public void SetFinished()
=> Interlocked.Exchange(ref _flag, 0);
}
I think it's the safest if you use it like this, with a 'Try' method, which tries to set the value and tells you if it was already set, in an atomic way.
Besides simply adding this (I mean just the field and the methods) to your existing component, you can also use it as a separate component, injected from the IOC container as scoped. Or even injected as a singleton, and then you don't have to use a static field.
Storing state like this should be good for as long as the application is running, but if the hosted application is recycled due to inactivity, it's obviously lost. Though, that won't happen while a request is still processing, and definitely won't happen in 5 seconds.
(And if you wanted to synchronize between app service instances, you could 'quickly' save a flag to the database, in a transaction with proper isolation level set. Or use e.g. Azure Redis Cache.)
Example solution
As Kit noted, rightly so, I didn't provide a full solution above.
So, a crude implementation could go like this:
public class SomeQueryService : ISomeQueryService
{
private static int _hasStartedFlag;
private static bool TrySetStarted()
=> Interlocked.CompareExchange(ref _hasStartedFlag, 1, 0) == 0;
private static void SetFinished()
=> Interlocked.Exchange(ref _hasStartedFlag, 0);
public async Task<(bool couldExecute, object result)> TryExecute()
{
if (!TrySetStarted())
return (couldExecute: false, result: null);
// Safely execute long query.
SetFinished();
return (couldExecute: true, result: result);
}
}
// In the controller, obviously
[HttpGet()]
public async Task<IActionResult> DoLongQuery([FromServices] ISomeQueryService someQueryService)
{
var (couldExecute, result) = await someQueryService.TryExecute();
if (!couldExecute)
{
return new ObjectResult(new ProblemDetails
{
Status = StatusCodes.Status503ServiceUnavailable,
Title = "Another request has already started. Try again later.",
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.4"
})
{ StatusCode = StatusCodes.Status503ServiceUnavailable };
}
return Ok(result);
}
Of course possibly you'd want to extract the 'blocking' logic from the controller action into somewhere else, for example an action filter. In that case the flag should also go into a separate component that could be shared between the query service and the filter.
General use action filter
I felt bad about my inelegant solution above, and I realized that this problem can be generalized into basically a connection number limiter on an endpoint.
I wrote this small action filter that can be applied to any endpoint (multiple endpoints), and it accepts the number of allowed connections:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ConcurrencyLimiterAttribute : ActionFilterAttribute
{
private readonly int _allowedConnections;
private static readonly ConcurrentDictionary<string, int> _connections = new ConcurrentDictionary<string, int>();
public ConcurrencyLimiterAttribute(int allowedConnections = 1)
=> _allowedConnections = allowedConnections;
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var key = context.HttpContext.Request.Path;
if (_connections.AddOrUpdate(key, 1, (k, v) => ++v) > _allowedConnections)
{
Close(withError: true);
return;
}
try
{
await next();
}
finally
{
Close();
}
void Close(bool withError = false)
{
if (withError)
{
context.Result = new ObjectResult(new ProblemDetails
{
Status = StatusCodes.Status503ServiceUnavailable,
Title = $"Maximum {_allowedConnections} simultaneous connections are allowed. Try again later.",
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.4"
})
{ StatusCode = StatusCodes.Status503ServiceUnavailable };
}
_connections.AddOrUpdate(key, 0, (k, v) => --v);
}
}
}

Synchronizing asynchronous events

I have a class that receives standard .Net events from an external class.
These events have an address property (in addition to a lot of other properties, of course) that I can use to synchronize my events, so that I should be able to create a method to Get something, wait for the correct event, then return the data from the event in the Get method.
However, I'm fairly new to synchronization in C# and was hoping any of you could help me out. Below is somewhat pseudo code for what I want to accomplish:
Someone calls DoAsynchronousToSynchronousCall
That method waits until an event have been received with the same address (or until it times out)
The event checks against all current requests. If it finds a request with the same address, let DoAsynchronousToSynchronousCall know the reply has arrived
DoAsynchronousCall gets (or retrieves) the reply and returns it to the caller
public class MyMessage
{
public string Address { get; set; }
public string Data { get; set; }
}
public Main
{
externalClass.MessageReceived += MessageReceived;
}
public void MessageReceived(MyMessage message)
{
MyMessage request = _requestQueue.FirstOrDefault(m => m.Address = message.Address);
if (request != null)
{
// Do something to let DoAsynchronousToSynchronousCall() know the reply has arrived
}
}
private List<MyMessage> _requestQueue = new List<MyMessage>();
public MyMessage DoAsynchronousToSynchronousCall(MyMessage message)
{
_requestQueue.Add(message);
externalClass.Send(message);
// Do something to wait for a reply (as checked for above)
MyMessage reply = WaitForCorrectReply(timeout: 10000);
return reply;
}
I feel like I'm missing an opportunity to use async and await (yet I don't know how), and I hope you're able to understand what I'm trying to accomplish based on the information above.
You really can't have multiple calls on the fly and have synchronous responses. If you want synchronous responses for multiple calls then you need to do the calls synchronously too.
I would look at using Microsoft's Reactive Extensions (NuGet "Rx-Main") to make what you're doing as simple as possible. Rx lets you turn events into streams of values that you can query against.
Here's what I would do.
I would first define a stream of the received messages as IObservable<MyMessage> receivedMessages like this:
receivedMessages =
Observable
.FromEvent<MessageReceivedHandler, MyMessage>(
h => externalClass.MessageReceived += h,
h => externalClass.MessageReceived -= h);
(You didn't provide a class def so I've called the event delegate MessageReceivedHandler.)
Now you can redefine DoAsynchronousToSynchronousCall as:
public IObservable<MyMessage> DoAsynchronousCall(MyMessage message)
{
return Observable.Create<MyMessage>(o =>
{
IObservable<MyMessage> result =
receivedMessages
.Where(m => m.Address == message.Address)
.Take(1);
IObservable<MyMessage> timeout =
Observable
.Timer(TimeSpan.FromSeconds(10.0))
.Select(x => (MyMessage)null);
IDisposable subscription =
Observable
.Amb(result, timeout)
.Subscribe(o);
externalClass.Send(message);
return subscription;
});
}
The result observable is the receivedMessages filtered for the current message.Address.
The timeout observable is a default value to return if the call takes longer than TimeSpan.FromSeconds(10.0) to complete.
Finally the subscription uses Observable.Amb(...) to determine which of result or timeout produces a value first and subscribes to that result.
So now to call this you can do this:
DoAsynchronousCall(new MyMessage() { Address = "Foo", Data = "Bar" })
.Subscribe(response => Console.WriteLine(response.Data));
So, if I make a simple definition of ExternalClass like this:
public class ExternalClass
{
public event MessageReceivedHandler MessageReceived;
public void Send(MyMessage message)
{
this.MessageReceived(new MyMessage()
{
Address = message.Address,
Data = message.Data + "!"
});
}
}
...I get the result Bar! printed on the console.
If you have a whole bunch of messages that you want to process you can do this:
var messagesToSend = new List<MyMessage>();
/* populate `messagesToSend` */
var query =
from message in messagesToSend.ToObservable()
from response in DoAsynchronousCall(message)
select new
{
message,
response
};
query
.Subscribe(x =>
{
/* Do something with each correctly paired
`x.message` & `x.response`
*/
});
You're probably looking for ManualResetEvent which functions as a "toggle" of sorts to switch between thread-blocking and non-blocking behavior. The DoAsynchronousToSynchronousCall would Reset and then WaitOne(int timeoutMilliseconds) the event to block the thread, and the thing checking for the correct reply arrived would do the Set call to let the thread continue on its way if the correct thing arrived.

'Flushing' observable Scan

This is a weird 'problem', I'm not sure what the best way to handle it is.
To simplify, let's say that I've got an observable source with some data reading coming from 'outside':
{ Value, TimeStamp }
I'm putting that through Observable.Scan so that I can output:
{ Value, TimeStamp, TimeDelta }
This means that my data always comes out 'one late', but that's not a problem.
We're 'recording' from this observable, and when you stop one recording, there's still one data value 'stuck' waiting for it's follower.
Even that's not a problem. The problem is that when you go to start recording again, the last value from the previous 'recording' gets stuck on to the beginning of the new one.
The most obvious thing to do is just to unsubscribe and resubscribe, but.... it's not that simple, because this scanned source is not only recorded, but also sent to the UI, and used for further calculations down the line: so I'd have to do an enormous unsubscribe/resubscribe.
I'm trying to think of a way to inject some kind of 'reset' data, but not sure how one goes about sending information back 'up' the observable stream...
Maybe I've just bitten off more than I can chew? Or used too much Observable?
There are going to be a number of ways to do this, but one that is fairly easy is to use the .Switch() operator.
It essentially works like this: if you have an IObservable<IObservable<T>> you can then call .Switch() to turn it into an IObservable<T> where it basically subscribes to the last value produced by the outer observable and unsubscribes to the previously produced observable.
Now that sounds a bit funky, but here's how it can work. Given you have an observable called outsideObservable then you defining a second observable (resubscribeObservable) that produces a value every time you want to resubscribe, and you subscribe to them like this:
var subscription =
resubscribeObservable
.Select(_ => outsideObservable)
.Switch()
.Subscribe(x =>
{
/* Do stuff here */
});
Now to resubscribe to outsideObservable you just have to produce a value from resubscribeObservable.
The easiest way to do this is to define it like var resubscribeObservable = new Subject<Unit>(); and then call resubscribeObservable.OnNext(Unit.Default); every time you want to resubscribe.
Alternatively if you have some event, say a user clicking a button, then you could use an observable based on that event as your resubscribeObservable.
Integrating suggestions from the comments, this would look something like:
var factory = Observable.Defer(() => outsideObservable);
var resetterObservable = new Subject<Unit>();
var resettableObservable =
resetterObservable
.StartWith(Unit.Default)
.Select(_ => factory)
.Switch()
.Publish()
.RefCount();
The Publish().RefCount() is just to protect the outsideObservable from multiple simultaneous subscriptions.
This is what I've boiled the accepted answer down to. Not yet in production, but tests seem to show it does what I want.
public interface IResetter
{
IObservable<T> MakeResettable<T>(Func<IObservable<T>> selector);
}
public class Resetter : IResetter
{
private Subject<Unit> _Resetter = new Subject<Unit>();
public void Reset()
{
_Resetter.OnNext(Unit.Default);
}
public IObservable<T> MakeResettable<T>(Func<IObservable<T>> selector)
{
return
_Resetter
.StartWith(Unit.Default)
.Select(_ => Observable.Defer(selector))
.Switch()
.Publish().RefCount();
}
}

Creating generated sequence of events as a cold sequence

FWIW - I'm scrapping the previous version of this question in favor of different one along the same way after asking for advice on meta
I have a webservice that contains configuration data. I would like to call it at regular intervals Tok in order to refresh the configuration data in the application that uses it. If the service is in error (timeout, down, etc) I want to keep the data from the previous call and call the service again after a different time interval Tnotok. Finally I want the behavior to be testable.
Since managing time sequences and testability seems like a strong point of the Reactive Extensions, I started using an Observable that will be fed by a generated sequence. Here is how I create the sequence:
Observable.Generate<DataProviderResult, DataProviderResult>(
// we start with some empty data
new DataProviderResult() {
Failures = 0
, Informations = new List<Information>()},
// never stop
(r) => true,
// there is no iteration
(r) => r,
// we get the next value from a call to the webservice
(r) => FetchNextResults(r),
// we select time for next msg depending on the current failures
(r) => r.Failures > 0 ? tnotok : tok,
// we pass a TestScheduler
scheduler)
.Suscribe(r => HandleResults(r));
I have two problems currently:
It looks like I am creating a hot observable. Even trying to use Publish/Connect I have the suscribed action missing the first event. How can I create it as a cold observable?
myObservable = myObservable.Publish();
myObservable.Suscribe(r => HandleResults(r));
myObservable.Connect() // doesn't call onNext for first element in sequence
When I suscribe, the order in which the suscription and the generation seems off, since for any frame the suscription method is fired before the FetchNextResults method. Is it normal? I would expect the sequence to call the method for frame f, not f+1.
Here is the code that I'm using for fetching and suscription:
private DataProviderResult FetchNextResults(DataProviderResult previousResult)
{
Console.WriteLine(string.Format("Fetching at {0:hh:mm:ss:fff}", scheduler.Now));
try
{
return new DataProviderResult() { Informations = dataProvider.GetInformation().ToList(), Failures = 0};
}
catch (Exception)
{}
previousResult.Failures++;
return previousResult;
}
private void HandleResults(DataProviderResult result)
{
Console.WriteLine(string.Format("Managing at {0:hh:mm:ss:fff}", scheduler.Now));
dataResult = result;
}
Here is what I'm seeing that prompted me articulating these questions:
Starting at 12:00:00:000
Fetching at 12:00:00:000 < no managing the result that has been fetched here
Managing at 12:00:01:000 < managing before fetching for frame f
Fetching at 12:00:01:000
Managing at 12:00:02:000
Fetching at 12:00:02:000
EDIT: Here is a bare bones copy-pastable program that illustrates the problem.
/*using System;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using Microsoft.Reactive.Testing;*/
private static int fetchData(int i, IScheduler scheduler)
{
writeTime("fetching " + (i+1).ToString(), scheduler);
return i+1;
}
private static void manageData(int i, IScheduler scheduler)
{
writeTime("managing " + i.ToString(), scheduler);
}
private static void writeTime(string msg, IScheduler scheduler)
{
Console.WriteLine(string.Format("{0:mm:ss:fff} {1}", scheduler.Now, msg));
}
private static void Main(string[] args)
{
var scheduler = new TestScheduler();
writeTime("start", scheduler);
var datas = Observable.Generate<int, int>(fetchData(0, scheduler),
(d) => true,
(d) => fetchData(d, scheduler),
(d) => d,
(d) => TimeSpan.FromMilliseconds(1000),
scheduler)
.Subscribe(i => manageData(i, scheduler));
scheduler.AdvanceBy(TimeSpan.FromMilliseconds(3000).Ticks);
}
This outputs the following:
00:00:000 start
00:00:000 fetching 1
00:01:000 managing 1
00:01:000 fetching 2
00:02:000 managing 2
00:02:000 fetching 3
I don't understand why the managing of the first element is not picked up immediately after its fetching. There is one second between the sequence effectively pulling the data and the data being handed to the observer. Am I missing something here or is it expected behavior? If so is there a way to have the observer react immediately to the new value?
You are misunderstanding the purpose of the timeSelector parameter. It is called each time a value is generated and it returns a time which indicates how long to delay before delivering that value to observers and then generating the next value.
Here's a non-Generate way to tackle your problem.
private DataProviderResult FetchNextResult()
{
// let exceptions throw
return dataProvider.GetInformation().ToList();
}
private IObservable<DataProviderResult> CreateObservable(IScheduler scheduler)
{
// an observable that produces a single result then completes
var fetch = Observable.Defer(
() => Observable.Return(FetchNextResult));
// concatenate this observable with one that will pause
// for "tok" time before completing.
// This observable will send the result
// then pause before completing.
var fetchThenPause = fetch.Concat(Observable
.Empty<DataProviderResult>()
.Delay(tok, scheduler));
// Now, if fetchThenPause fails, we want to consume/ignore the exception
// and then pause for tnotok time before completing with no results
var fetchPauseOnErrors = fetchThenPause.Catch(Observable
.Empty<DataProviderResult>()
.Delay(tnotok, scheduler));
// Now, whenever our observable completes (after its pause), start it again.
var fetchLoop = fetchPauseOnErrors.Repeat();
// Now use Publish(initialValue) so that we remember the most recent value
var fetchLoopWithMemory = fetchLoop.Publish(null);
// YMMV from here on. Lets use RefCount() to start the
// connection the first time someone subscribes
var fetchLoopAuto = fetchLoopWithMemory.RefCount();
// And lets filter out that first null that will arrive before
// we ever get the first result from the data provider
return fetchLoopAuto.Where(t => t != null);
}
public MyClass()
{
Information = CreateObservable();
}
public IObservable<DataProviderResult> Information { get; private set; }
Generate produces cold observable sequences, so that is my first alarm bell.
I tried to pull your code into linqpad* and run it and changed it a bit to focus on the problem. It seems to me that you have the Iterator and ResultSelector functions confused. These are back-to-front. When you iterate, you should take the value from your last iteration and use it to produce your next value. The result selector is used to pick off (Select) the value form the instance you are iterating on.
So in your case, the type you are iterating on is the type you want to produce values of. Therefore keep your ResultSelector function just the identity function x=>x, and your IteratorFunction should be the one that make the WebService call.
Observable.Generate<DataProviderResult, DataProviderResult>(
// we start with some empty data
new DataProviderResult() {
Failures = 0
, Informations = new List<Information>()},
// never stop
(r) => true,
// we get the next value(iterate) by making a call to the webservice
(r) => FetchNextResults(r),
// there is no projection
(r) => r,
// we select time for next msg depending on the current failures
(r) => r.Failures > 0 ? tnotok : tok,
// we pass a TestScheduler
scheduler)
.Suscribe(r => HandleResults(r));
As a side note, try to prefer immutable types instead of mutating values as you iterate.
*Please provide an autonomous working snippet of code so people can better answer your question. :-)

How to validate certain callbacks were called asynchronously (in order)

I am developing some system that involves reading messages from some server, registering event handlers and invoking the handlers in turn.
I am using .NET 3.5 so mind you that solution from .NET 4 or async are not available.
The code is similar to this (removed some pieces for brevity):
// Loop until receiving timeout.
while (!timedOut)
{
if ((message = connection.Receive) != null)
{
if (message.Id == Error.MessageId)
{
throw new Exception("Something wrong happened!!");
}
// This calls the event handler (if registered).
HandleEvent(message);
if (finishedEvents)
{
// Finished.
}
else
{
// If message matches the requested message.
if (message.Id == expectedEvents[index].MessageId)
{
index++;
}
// Finished processing all messages.
if (index == expectedEvents.Length)
{
finishedEvents = true;
continue;
}
}
else
{
break;
}
}
Currently i have a synchronous (blocking) implementation where i have a loop that reads the messages and fires the correct handler.
This way, i can verify easily that events are being fired IN ORDER.
I would like to switch to an asynchronous implementation where the events will be fired asynchronously, instead of reading them in a loop.
How can i verify in this implementation that events are received in order? is there a standard solution to this problem?
Or another solution us Reactive Framework, and use the async calls collection in ForEach loop, like an ordinary one.
See this answer and online documentation for samples.
I think the general design should be something like this (pseudo code):
int currentIndex = 0;
List<myObject> lst = getList();
executor.BeginAsyncTask(lst[currentIndex], AsyncTaskCallBack)
function AsyncTaskCallBack(result)
{
if(result.IsOK)
{
currentIndex+=1;
if(currentIndex < lst.Count)
executor.BeginAsyncTask(lst[currentIndex], AsyncTaskCallBack)
}
}
Using that design, you will process next object only when previous object is finished and it will not block your code.
In case you want to do it in ASP.NET, the implemintation may be some different...while you waiting for AsyncTaskCallBack execution, the request proccesing may be finished and already sent tot browser.

Categories

Resources