I'm stumped on how to implement a cancellation / retry mechanism for the following code, it is mostly pseudo-code for brevity:
class CLI {
async void Main() {
var response = await bot.GetSomething(); // final usage
}
}
class Bot {
private void StartTask() {
_taskCompSource = new TaskCompletionSource<object>();
}
private async Task<T> ResultPromise<T>() {
return await _taskCompSource.Task.ContinueWith(t => t.Result != null ? (T)t.Result : default(T));
}
// send request
public Task<string> GetSomething() {
StartTask();
QueueRequest?.Invoke(this, new Request(...)); // complex non-sync request, fired off here
return ResultPromise<string>();
}
// receive response
public void Process(Reply reply) {
// process reply
_taskCompSource.SetResult("success");
}
}
class BotManager {
// bot.QueueRequest += QueueRequest;
// _service.ReplyReceived += ReplyReceived;
private void QueueRequest(object sender, Request request) {
_service.QueueRequestForSending(request);
}
private async void ReplyReceived(object sender, Reply reply) {
GetBot().Process(reply);
}
}
class Service {
private Dictionary<string, Queue<Request>> _queues;
// send receive loop
private async void HttpCallback(IAsyncResult result) {
while (true) {
// if reply received then call ReplyReceived?.Invoke(this, reply);
// check if request already in progress // ##ISSUE IS HERE##
// if not send another request
// keep connection to server open by feeding spaces
}
}
public void QueueRequestForSending(Request request) {
GetQueueForBot().Enqueue(request);
}
}
I would like to implement a timeout on await bot.GetSomething();
but not sure how to do it with this kind of disconnected nature. I tried:
static class Extensions {
private static async Task<T> RunTaskWithRetry<T>(Func<Task<T>> taskFunc, int retries, int timeout) {
do {
var task = taskFunc(); // just adds another request to queue which never gets ran because the previous one is waited forever to return, this needs to cancel the previous request, from here, but how?
await Task.WhenAny(task, Task.Delay(timeout)).ConfigureAwait(false);
if (task.Status == TaskStatus.RanToCompletion) {
return task.Result;
}
retries--;
}
while (retries > 0);
return default(T);
}
public async static Task<T> WithRetry<T>(this Task<T> task, int retries = 3, int timeout = 10000) {
return await RunTaskWithRetry(async () => await task, retries, timeout);
}
}
Which can be called like await bot.GetSomething().WithRetry();
but the issue here is it just adds another request to the queue and not cancel or remove the existing one. To cancel, I would simply have to remove the existing request from the awaiting
list. Problem is I do not know how to do it all the way from where the extension method is.
I would like to know any possible mechanism I can use to achieve timeout and cancellation.