I wrote a static class which tests the internet connectivity of the user, and raises an event if their connection status changes:
class InternetConnectionMonitor
{
public static EventHandler<EventArgs<bool>> StatusChanged;
private static bool isCancelled;
private static bool isConnected;
private static bool IsConnected
{
get
{
return isConnected;
}
set
{
if (isConnected != value)
{
StatusChanged(null, new EventArgs<bool>(value));
}
isConnected = value;
}
}
public static async void Start(TimeSpan interval)
{
//TODO Use a 1st party webpage for connectivity testing.
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(2);
while (!isCancelled)
{
try
{
await client.GetAsync("http://example.com");
IsConnected = true;
}
catch (Exception)
{
IsConnected = false;
}
await Task.Delay(interval);
}
}
}
public static void Stop()
{
isCancelled = true;
}
}
This class works wonderfully, however, when doing some other processing using the TPL Dataflow in my app, an exception is raised in the while loop of the Start() method saying the task was cancelled. The reason I am posting here, is because I never cancel any tasks.
Here is the processing I am doing. The task cancelled exception is raised in InternetConnectionMonitor after QueueTests is completed, although QueueTests makes no reference to InternetConnectionMonitor whatsoever.
If I don't call validateProxies(), the exception is never raised.
private async void validateProxies(IEnumerable<Proxy> proxies)
{
validateProxiesButton.Enabled = false;
cancelValidatingProxiesButton.Enabled = true;
addProxiesButton.Enabled = false;
removeProxiesButton.Enabled = false;
proxyTester = new ProxyTester();
await proxyTester.QueueTests(proxies, judges);
validateProxiesButton.Enabled = true;
cancelValidatingProxiesButton.Enabled = false;
addProxiesButton.Enabled = true;
removeProxiesButton.Enabled = true;
MessageBox.Show("Complete!");
}
public class ProxyTester
{
private PauseOrCancelTokenSource pcts = new PauseOrCancelTokenSource();
public async Task QueueTests(IEnumerable<Proxy> proxies, IEnumerable<ProxyJudge> judges, int maxConcurrency = 100)
{
var testProxies = new ActionBlock<(Proxy proxy, IProxyTest test)>((tup) =>
{
tup.proxy.Status = ProxyStatus.Testing;
tup.proxy.Status = tup.proxy.TestValidity(tup.test);
}, new ExecutionDataflowBlockOptions { CancellationToken = pcts.Token.CancellationToken, MaxDegreeOfParallelism = maxConcurrency });
//Set each proxies status to Queued, and post to the dataflow block.
foreach (var proxy in proxies)
{
proxy.Status = ProxyStatus.Queued;
await testProxies.SendAsync((proxy, judges.GetRandomItem()));
}
testProxies.Complete();
try
{
await testProxies.Completion;
}
catch (Exception)
{
}
}
public void Cancel()
{
pcts.Cancel();
}
}
Starting InternetConnectionMonitor (requested by JleruOHeP in comments)
public proxyTesterView()
{
InitializeComponent();
InternetConnectionMonitor.StatusChanged += InternetConnectionMonitor_StatusChanged;
InternetConnectionMonitor.Start(TimeSpan.FromSeconds(1));
}
private void InternetConnectionMonitor_StatusChanged(object sender, EventArgs<bool> e)
{
if (e.Value == true)
{
MessageBox.Show("Online");
}
else
{
MessageBox.Show("Offline");
}
}
Solved my own question and wanted to share my solution. After some thinking, I had a feeling perhaps threadpool threads were becoming exhausted when calling QueueTests, due to the high default max degree of parallelism (100). This threadpool exhaustion seemed to have unintended side effects on the call to client.GetAsync in the Start() method in InternetConnectionMonitor, causing the timeout on the request to improperly trigger, thus causing the TaskCancelledException.
So I spawned my own explicit thread and am synchronously performing the testing within it. Exception gone and working as intended now.
public static void Start()
{
//TODO Use a 1st party webpage for connectivity testing.
var t = new Thread(() =>
{
while (!isCancelled)
{
try
{
var req = HttpWebRequest.Create("http://example.com");
req.Timeout = 1000;
using (var resp = req.GetResponse())
{
resp.Close();
}
IsConnected = true;
}
catch (Exception ex)
{
IsConnected = false;
}
Console.WriteLine(IsConnected);
Thread.Sleep(1000);
}
});
t.Start();
}
Related
I want to write some Unittests with NUnit for our wpf application.
The application downloads some data with System.Net.WebClient in the background using the observer pattern.
Here is an example:
Download.cs
public class Download : IObservable<string>
{
private string url { get; }
private List<IObserver<string>> observers = new List<IObserver<string>>();
private bool closed = false;
private string data = null;
public Download(string url)
{
this.url = url;
startDownload();
}
public IDisposable Subscribe(IObserver<string> observer)
{
if (!observers.Contains(observer))
{
if (!closed)
{
observers.Add(observer);
}
else
{
sendAndComplete(observer);
}
}
return new Unsubscriber(observer, observers);
}
private void startDownload()
{
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler((object sender, DownloadStringCompletedEventArgs e) => {
if (e.Error != null)
{
data = e.Result;
}
closed = true;
sendAndComplete();
});
client.DownloadStringAsync(new Uri(url));
}
private void sendAndComplete()
{
foreach (var observer in observers)
{
sendAndComplete(observer);
}
observers.Clear();
}
private void sendAndComplete(IObserver<string> observer)
{
if (data != null)
{
observer.OnNext(data);
}
else
{
observer.OnError(new Exception("Download failed!"));
}
observer.OnCompleted();
}
private class Unsubscriber : IDisposable
{
private IObserver<string> _observer { get; }
private List<IObserver<string>> _observers { get; }
public Unsubscriber(IObserver<string> _observer, List<IObserver<string>> _observers)
{
this._observer = _observer;
this._observers = _observers;
}
public void Dispose()
{
if (_observer != null && _observers.Contains(_observer))
{
_observers.Remove(_observer);
}
}
}
}
DownloadInspector.cs
public class DownloadInspector : IObserver<string>
{
private Action<string> onSuccessAction { get; }
private Action<Exception> onErrorAction { get; }
private Action onCompleteAction { get; }
public DownloadInspector(Action<string> onSuccessAction, Action<Exception> onErrorAction, Action onCompleteAction)
{
this.onSuccessAction = onSuccessAction;
this.onErrorAction = onErrorAction;
this.onCompleteAction = onCompleteAction;
}
public void OnCompleted()
{
onCompleteAction.Invoke();
}
public void OnError(Exception error)
{
onErrorAction.Invoke(error);
}
public void OnNext(string value)
{
onSuccessAction.Invoke(value);
}
}
example (usage)
Download download = new Download("http://stackoverflow.com");
DownloadInspector inspector = new DownloadInspector(
(string data) =>
{
Debug.WriteLine("HANDLE DATA");
},
(Exception error) =>
{
Debug.WriteLine("HANDLE ERROR");
},
() =>
{
Debug.WriteLine("HANDLE COMPLETE");
}
);
I'm still new in c# and not very familiar with asynchronous programming in that language. I know the await and async keywords and know that they work with NUnit, but the current construct don't use this keywords.
Can you help me creating a unit test for this case? Its okay to change/remove the observer pattern.
The constructor for the Download class starts the download, which means that I can't subscribe an observer until after the download has started. That's a race condition. It's possible (although unlikely) that observers will be notified before they can be subscribed.
public Download(string url)
{
this.url = url;
startDownload();
}
But I can go ahead and test because I'm subscribing an observer before that can happen. If you can I'd recommend not doing that. Allow the caller to construct the class in one step and then start the download with a method call.
I also had to change this method. I figured that testing for an error would be the easiest first step, but it needs to do data = e.Result if there is no error, not if there is an error.
private void StartDownload()
{
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler((object sender, DownloadStringCompletedEventArgs e) =>
{
if (e.Error == null) // <== because of this
{
data = e.Result;
}
closed = true;
sendAndComplete();
});
client.DownloadStringAsync(new Uri(url));
}
What I didn't see coming is that WebClient.DownloadStringAsync isn't actually async. It doesn't return a Task. It just takes a callback. What that means is that there's no sure way to know whether it's done except to wait for it to notify the observer that the download is complete.
My NUnit test runner wasn't running, so I used MsTest. It's the same thing.
The basic approach is that I'm creating some flags, and the inspector responds to notifications by setting the flags. That way I can see which notifications were raised.
The last problem is that because DownloadStringComplete is a callback, the test exits before the Assert. That means it will always pass. So in order to fix it I had to do something I've never seen before, which I found here:
[TestMethod]
public void download_raises_error_notification()
{
var success = false;
bool error = false;
bool complete = false;
var pause = new ManualResetEvent(false);
var download = new Download("http://NoSuchUrlAnywhere.com");
var inspector = new DownloadInspector(
onSuccessAction: s => success = true,
onCompleteAction: () =>
{
complete = true;
pause.Set();
},
onErrorAction: s => error = true
);
download.Subscribe(inspector);
// allow 500ms for the download to fail. This is a race condition.
pause.WaitOne(500);
Assert.IsTrue(error,"onErrorAction was not called.");
}
This is technically an integration test since it must actually attempt a download in order to run. That could be remedied by mocking WebClient.
I have a problem, and I am not quite sure how to solve this, except for making my Akka Actor not have async methods.
Here is my Actor Code:
public class AggregatorActor : ActorBase, IWithUnboundedStash
{
public IStash Stash { get; set; }
private AggregatorTimer _aggregatorTimer;
private IActorSystemSettings _settings;
private AccountSummary _accountResponse;
private ContactDetails _contactResponse;
private AnalyticDetails _analyticsResponse;
private FinancialDetails _financialResponse;
private ActorSelection _accountActor;
private ActorSelection _contactActor;
private ActorSelection _analyticsActor;
private ActorSelection _financialActor;
public AggregatorActor(IActorSystemSettings settings) : base(settings)
{
_accountActor = Context.System.ActorSelection(ActorPaths.AccountActorPath);
_contactActor = Context.System.ActorSelection(ActorPaths.ContactActorPath);
_analyticsActor = Context.System.ActorSelection(ActorPaths.AnalyticsActorPath);
_financialActor = Context.System.ActorSelection(ActorPaths.FinancialActorPath);
_settings = settings;
}
#region Public Methods
public override void Listening()
{
ReceiveAsync<ProfilerMessages.ProfilerBase>(async x => await HandleMessageAsync(x));
}
private void Busy()
{
Receive<ProfilerMessages.ProfilerBase>(x => Stash.Stash());
}
private void Aggregate()
{
try
{
Context.Sender.Tell(AggregatedSummaryResponse.Instance(_accountResponse, _contactResponse, _analyticsResponse, _financialResponse));
}
catch (Exception ex)
{
ExceptionHandler(ex);
}
}
public override async Task HandleMessageAsync(object msg)
{
//if is summary, generate new isntance of AggregatorTimer in _eventHandlerCollection.
if (msg is ProfilerMessages.GetSummary)
{
//Become busy. Stash
Become(Busy);
//Handle different requests
var clientId = (msg as ProfilerMessages.GetSummary).ClientId;
await HandleSummaryRequest(clientId);
}
}
private async Task HandleSummaryRequest(string clientId)
{
try
{
var accountMsg = new AccountMessages.GetAggregatedData(clientId);
_accountResponse = (await _accountActor.Ask<Messages.AccountMessages.AccountResponseAll>(accountMsg, TimeSpan.FromSeconds(_settings.NumberOfSecondsToWaitForResponse))).AccountDetails;
//Need to uncomment this
var contactMsg = new ContactMessages.GetAggregatedContactDetails(clientId);
_contactResponse = (await _contactActor.Ask<Messages.ContactMessages.ContactResponse>(contactMsg, TimeSpan.FromSeconds(_settings.NumberOfSecondsToWaitForResponse))).ContactDetails;
var analyticMsg = new AnalyticsMessages.GetAggregatedAnalytics(clientId);
_analyticsResponse = (await _analyticsActor.Ask<Messages.AnalyticsMessages.AnalyticsResponse>(analyticMsg, TimeSpan.FromSeconds(_settings.NumberOfSecondsToWaitForResponse))).AnalyticDetails;
var financialMsg = new FinancialMessages.GetAggregatedFinancialDetails(clientId);
_financialResponse = (await _financialActor.Ask<Messages.FinancialMessages.FinancialResponse>(financialMsg, TimeSpan.FromSeconds(_settings.NumberOfSecondsToWaitForResponse))).FinancialDetails;
//Start new timer
_aggregatorTimer = new AggregatorTimer(_settings.NumberOfSecondsToWaitForResponse);
_aggregatorTimer.TimeElapsed += _aggregatorTimer_TimeElapsed;
}
catch (Exception ex)
{
ExceptionHandler(ex);
}
}
//Event that is raised when an external timers time elapsed.
private async void _aggregatorTimer_TimeElapsed(object sender, ElapsedTimeHandlerArg e)
{
Aggregate();
_aggregatorTimer = null;
_accountResponse = null;
_contactResponse = null;
_analyticsResponse = null;
_financialResponse = null;
//Unstash
Stash.Unstash();
//Start listening again
Become(Listening);
}
#endregion
}
Inside the _aggregatorTimer_TimeElapsed event, I call the await Aggregate function, but the following exception is thrown.
There is no active ActorContext, this is most likely due to use of async operations from within this actor
I think this is caused by the fact that the Aggregate function tries to Tell() the Sender about the responses that are aggregated, but those Tasksare not yet completed? I might be completely wrong, but I have no idea why this is thrown.
I'm not sure what do you even need an AggregatorTimer for - in akka you have a Context.System.Scheduler object, which can be used to schedule events going to happen in the future.
Another thing is that you probably shouldn't execute logic inside event handlers. If you really need them in your code, it's better to limit them only to send a message, once an event gets triggered i.e.:
Receive<TimedOut>(_ => /* handle timeout message */);
var self = Self; // bind current self to a variable, so it won't change
_aggregatorTimer.TimeElapsed += (sender, e) => self.Tell(new TimedOut(), self);
I have implemented IHttpAsyncHandler in my class to perform 5-6 long running process in background and acknowledge to client on start of each task.
Earlier I was using one session variable and updating it with current status of task, and giving async call request to server from jquery in interval of 5 seconds to get current status, but this implementation is not good because its continually hitting request to server for status.
Then I implemented IHttpAsyncHandler in my application, now server itself send acknowledgement to client, but as per my implementation I am able to send only one acknowledgement! if I try to send more than one then its giving error as "object reference not set to an instance of an object"
please check my sample code.
in my code
ExecuteFirst() method works fine sending acknowledgement to client but ExecuteSecond() does not send acknowledgement its giving error.
I goggled a lot but not getting proper way to send multiple acknowledgement to client.
this is my sample code, please help me if any one have any idea.
Javascript
function postRequest(url) {
var url = 'StartLongRunningProcess.ashx?JOBCODE=18'
var xmlhttp = getRequestObject();
xmlhttp.open("POST", url, true);
xmlhttp.onreadystatechange =
function () {
if (xmlhttp.readyState == 4) {
var response = xmlhttp.responseText;
divResponse.innerHTML += "<p>" + response + "</p>";
}
}
xmlhttp.send();
}
function getRequestObject() {
var req;
if (window.XMLHttpRequest && !(window.ActiveXObject)) {
try {
req = new XMLHttpRequest();
}
catch (e) {
req = false;
}
}
else if (window.ActiveXObject) {
try {
req = new ActiveXObject('Msxml2.XMLHTTP');
}
catch (e) {
try {
req = new ActiveXObject('Microsoft.XMLHTTP');
}
catch (e) {
req = false;
}
}
}
return req;
}
StartLongRunningProcess.ashx.cs
public class StartLongRunningProcess: IHttpAsyncHandler, IRequiresSessionState
{
private AsyncRequestResult _asyncResult;
public void ProcessRequest(HttpContext context) {}
public bool IsReusable
{
get { return true;}
}
public IAsyncResult BeginProcessRequest(HttpContext context, System.AsyncCallback cb, object extraData)
{
int32 jobCode= convert.ToInt32(context.Request["JOBCODE"]);
_asyncResult = new AsyncRequestResult(context, cb, jobCode);
if(jobCode==18)
{
StartProcess18()
}
else
{
//StartProcessOther()
}
}
private StartProcess18()
{
var task1= new Task(() =>
{
ExecuteFirst();
});
var task2 = task1.ContinueWith((t1) =>
{
ExecuteSecond();
}, TaskContinuationOptions.OnlyOnRanToCompletion);
task1.Start();
}
private ExecuteFirst()
{
//notify to client that this job has been started
_asyncResult.CurrentContext.Response.Write("First task has been started");
_asyncResult.Notify();
// Above line of code successfully send a acknowledgement to client
//Doing some long running process
}
private ExecuteSecond()
{
//notify to client that this job has been started
_asyncResult.CurrentContext.Response.Write("Second task has been started");
// Above line of code giving error and if I skip it and call Notify() this also does not work.
_asyncResult.Notify();
//Doing some long running process
}
public void EndProcessRequest(IAsyncResult result)
{
}
}
AsyncRequestResult.cs
public class AsyncRequestResult : IAsyncResult
{
private HttpContext context;
private AsyncCallback callback;
private ManualResetEvent completeEvent = null;
private object data;
private object objLock = new object();
private bool isComplete = false;
public AsyncRequestResult(HttpContext ctx, AsyncCallback cb, object d)
{
this.context = ctx;
this.callback = cb;
this.data = d;
}
public HttpContext Context
{
get { return this.context; }
}
public void Notify()
{
//isComplete = true;
lock(objLock)
{
if(completeEvent != null)
{
completeEvent.Set();
}
}
if (callback != null)
{
callback(this);
}
}
public object AsyncState
{
get { return this.data; }
}
public bool CompletedSynchronously
{
get { return false; }
}
public WaitHandle AsyncWaitHandle
{
get
{
lock(objLock)
{
if (completeEvent == null)
completeEvent = new ManualResetEvent(false);
return completeEvent;
}
}
}
public bool IsCompleted
{
get { return this.isComplete; }
}
}
HttpContext.Current is not null only if you access it in a thread that handles incoming requests.
Your continuation task that is running is most likely running on a ThreadPool thread without the HttpContext.Current flowing to the continuation, hence it being null.
Try setting your TaskScheduler to TaskScheduler.FromCurrentSynchronizationContext() in order to execute it in the same context.
private StartProcess18()
{
var task1= new Task(() =>
{
ExecuteFirst();
});
var task2 = task1.ContinueWith((t1) =>
{
ExecuteSecond();
}, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
task1.Start();
}
I saw a lot of examples about GeginGetContext but i have filling that all of them are waste time. Maybe i am wrong. Lets find out. Lets take for instance really good example from the Multi-threading with .Net HttpListener topic:
public class HttpListenerCallbackState
{
private readonly HttpListener _listener;
private readonly AutoResetEvent _listenForNextRequest;
public HttpListenerCallbackState(HttpListener listener)
{
if (listener == null) throw new ArgumentNullException("listener");
_listener = listener;
_listenForNextRequest = new AutoResetEvent(false);
}
public HttpListener Listener { get { return _listener; } }
public AutoResetEvent ListenForNextRequest { get { return _listenForNextRequest; } }
}
public class HttpRequestHandler
{
private int requestCounter = 0;
private ManualResetEvent stopEvent = new ManualResetEvent(false);
public void ListenAsynchronously(IEnumerable<string> prefixes)
{
HttpListener listener = new HttpListener();
foreach (string s in prefixes)
{
listener.Prefixes.Add(s);
}
listener.Start();
HttpListenerCallbackState state = new HttpListenerCallbackState(listener);
ThreadPool.QueueUserWorkItem(Listen, state);
}
public void StopListening()
{
stopEvent.Set();
}
private void Listen(object state)
{
HttpListenerCallbackState callbackState = (HttpListenerCallbackState)state;
while (callbackState.Listener.IsListening)
{
callbackState.Listener.BeginGetContext(new AsyncCallback(ListenerCallback), callbackState);
int n = WaitHandle.WaitAny(new WaitHandle[] { callbackState.ListenForNextRequest, stopEvent});
if (n == 1)
{
// stopEvent was signalled
callbackState.Listener.Stop();
break;
}
}
}
private void ListenerCallback(IAsyncResult ar)
{
HttpListenerCallbackState callbackState = (HttpListenerCallbackState)ar.AsyncState;
HttpListenerContext context = null;
int requestNumber = Interlocked.Increment(ref requestCounter);
try
{
context = callbackState.Listener.EndGetContext(ar);
}
catch (Exception ex)
{
return;
}
finally
{
callbackState.ListenForNextRequest.Set();
}
if (context == null) return;
HttpListenerRequest request = context.Request;
if (request.HasEntityBody)
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(request.InputStream, request.ContentEncoding))
{
string requestData = sr.ReadToEnd();
//Stuff I do with the request happens here
}
}
try
{
using (HttpListenerResponse response = context.Response)
{
//response stuff happens here
string responseString = "Ok";
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.LongLength;
response.OutputStream.Write(buffer, 0, buffer.Length);
response.Close();
}
}
catch (Exception e)
{
}
}
}
Here we can see main part for this topic:
while (callbackState.Listener.IsListening)
{
callbackState.Listener.BeginGetContext(new AsyncCallback(ListenerCallback), callbackState);
int n = WaitHandle.WaitAny(new WaitHandle[] { callbackState.ListenForNextRequest, stopEvent});
...
}
I can saw this patterns in +- all examples.
We are starting Getting Contex (Getting request = getting network stream with request) after that we are blocking current thread with Wait/WaitAny method, so thread witch is getting request is doing nothing until it will got full request after that it will getting new request. For example we are having WCF
request with large object(which are deserialized in this request and are serialized in the same way on other side) so we will wait and WASTE TIME until we complete getting full steam with this request.
I see that really it is Sync not Async way. Instead of this we can starting getting second request
right after the starting getting first and not call Wait, not blocking thread. I think we will have much better performance in such way. What do you think? Why do all examples contains Wait?
I'm trying to create a quite simple notifications system (don't want to use SignalIR or something else). I have the following testing code:
Client side:
var source = new EventSource('/notifications.axd');
source.onopen = function () {
Console.log("Connection open");
};
source.onerror = function () {
Console.log("Connection error");
};
source.onmessage = function (event) {
Console.log("Message: " + event.data);
};
Server side:
public class NotificationMessage {
public NotificationMessage() {
Id = Guid.NewGuid().ToString();
}
public string Id { get; private set; }
}
public class NotificationsHandler : HttpTaskAsyncHandler {
private const string CONTENT_TYPE = "text/event-stream";
private sealed class NotificationItem {
public ConcurrentQueue<NotificationMessage> Messages;
public CancellationTokenSource CancellationTokenSource;
}
private static readonly ConcurrentDictionary<string, NotificationItem> _tasks =
new ConcurrentDictionary<string, NotificationItem>();
public static void Notify(string hostId, string userId, NotificationMessage message) {
NotificationItem item;
if (!_tasks.TryGetValue(string.Format("{0}|{1}", hostId, userId), out item)) {
return;
}
var tokenSource = item.CancellationTokenSource;
item.Messages.Enqueue(message);
item.CancellationTokenSource = new CancellationTokenSource();
tokenSource.Cancel();
}
public override async Task ProcessRequestAsync(HttpContext context) {
HttpRequest request = context.Request;
NotificationItem item = _tasks.GetOrAdd(
string.Format("{0}|{1}", request.Url.Host, CsSession.Data.CurrentUser.Id),
k => new NotificationItem {
Messages = new ConcurrentQueue<NotificationMessage>(),
CancellationTokenSource = new CancellationTokenSource()
}
);
HttpResponse response = context.Response;
response.ContentType = CONTENT_TYPE;
response.CacheControl = "no-cache";
response.ContentEncoding = Encoding.UTF8;
response.AppendHeader("connection", "keep-alive");
response.BufferOutput = false;
bool supportsAsyncFlush = response.SupportsAsyncFlush;
bool shouldPing = true;
while (response.IsClientConnected) {
try {
NotificationMessage message = null;
if ((!item.Messages.IsEmpty && item.Messages.TryDequeue(out message)) || shouldPing) {
response.Write(string.Format("data:{0}\n\n", message == null ? "{}" : JsonMapper.Serialize(message)));
if (supportsAsyncFlush) {
await Task.Factory.FromAsync(response.BeginFlush, response.EndFlush, null);
} else {
response.Flush();
}
}
} catch (Exception) {
break;
}
var delay = Task.Delay(15000, item.CancellationTokenSource.Token);
await delay;
shouldPing = delay.Status == TaskStatus.RanToCompletion;
}
}
}
The problem is: the above doesn't works. I have two issues:
1) When the client connects, I receive an empty packet (that's ok). Then, if I don't enqueue any messages, after awaiting the Task.Delay, the loop tries to write an empty message again, but I don't know where. The response.Write line never returns (and nothing is being received on the client).
2) If I write to the queue, for some reason the connection is dropped. If I put a breakpoint on the line after the await delay, that line is never executed (while my logic says otherwise :) ). If I cancel the token, the delay task should quit, but it seems it is aborting the whole handler??