How to combine time base polling with awaitable Task - c#

I implemented already a polling-worker based on Timer. As example you can think of TryConnect on the client side -- I call TryConnect and it will connect eventually in some time. It handles multiple threads, if connecting is in the process already all subsequent TryConnect returns immediately without any extra action. Internally I simply create a timer and in intervals I try to connect -- if the connection fails, I try again. And so on.
Small downside is it is "fire&forget" pattern and now I would like to combine it with "async/await" pattern, i.e. instead calling:
client.TryConnect(); // returns immediately
// cannot tell if I am connected at this point
I would like to call it like this:
await client.TryConnect();
// I am connected for sure
How can I change my implementation to support "async/await"? I was thinking about creating empty Task (just for await), and then complete it with FromResult, but this method creates a new task, it does not complete given instance.
For the record, current implementation looks like this (just a sketch of the code):
public void TryConnect()
{
if (this.timer!=null)
{
this.timer = new Timer(_ => tryConnect(),null,-1,-1);
this.timer.Change(0,-1);
}
}
private void tryConnect()
{
if (/*connection failed*/)
this.timer.Change(interval,-1);
else
this.timer = null;
}

Lacking a good Minimal, Complete, and Verifiable code example it's impossible to offer any specific suggestions. Given what you've written, it's possible what you're looking for is TaskCompletionSource. For example:
private TaskCompletionSource<bool> _tcs;
public async Task TryConnect()
{
if (/* no connection exists */)
{
if (_tcs == null)
{
this.timer = new Timer(_ => tryConnect(),null,-1,-1);
this.timer.Change(0,-1);
_tcs = new TaskCompletionSource<bool>();
}
await _tcs.Task;
}
}
private void tryConnect()
{
if (/*connection failed*/)
this.timer.Change(interval,-1);
else
{
_tcs.SetResult(true);
_tcs = null;
this.timer = null;
}
}
Notes:
Your original code example would retry the connection logic if TryConnect() is called again after a connection is made. I expect what you really want is to also check for the presence of a valid connection, so I modified the above slightly to check for that. You can of course remove that part if you actually always want to attempt a new connection, even if one already exists.
The code sets _tcs to null immediate after setting its result. Note though that any code awaiting or otherwise having stored the Task value for the _tcs object will implicitly reference the current _tcs object, so it's not a problem to discard the field's reference here.
There is no non-generic TaskCompletionSource. So for scenarios where you just need a Task, you can use the generic type with a placeholder type, like bool as I've done here or object or whatever. I could've called SetResult(false) just as well as SetResult(true), and it wouldn't matter in this example. All that matters is that the Task is completed, not what value is returned.
The above uses the async keyword to make TryConnect() an async method. IMHO this is a bit more readable, but of course does incur slight overhead in the extra Task to represent the method's operation. If you prefer, you can do the same thing directly without the async method:
public Task TryConnect()
{
if (/* no connection exists */)
{
if (_tcs == null)
{
this.timer = new Timer(_ => tryConnect(),null,-1,-1);
this.timer.Change(0,-1);
_tcs = new TaskCompletionSource<bool>();
}
return _tcs.Task;
}
return Task.CompletedTask;
}

Related

How to wait for a one of callbacks method response before continuing the code?

I am currently working on a system, which actively tracks servers information. For a few methods, I need it to be asynchronous, however, there are a few which are implemented in a synchronous manner (an external library). I have this code:
m_ServerQuery = HServerQuery.Invalid;
m_PingResponse = new ISteamMatchmakingPingResponse(OnServerResponded, OnServerFailedToRespond);
try
{
m_ServerQuery = SteamMatchmakingServers.PingServer((uint)ip, port, m_PingResponse);
await Task.Delay(500);
SteamMatchmakingServers.CancelServerQuery(m_ServerQuery);
}
catch
{
Console.WriteLine($"*** Something went wrong while pinging server ***");
}
As you can see from the code snippet above the PingResponse class inherits two methods which work as "callback" when a response is sent from steam. Now awaiting it for 0.5 milliseconds works, however, I think it would be better to implement it to wait for one of these two methods to trigger:
OnServerResponded, OnServerFailedToRespond
How would I be able to achieve that? The ISteamMatchmakingPingResponse definition:
public class ISteamMatchmakingPingResponse
{
public ISteamMatchmakingPingResponse(ServerResponded onServerResponded, ServerFailedToRespond onServerFailedToRespond);
~ISteamMatchmakingPingResponse();
public static explicit operator IntPtr(ISteamMatchmakingPingResponse that);
public delegate void ServerResponded(gameserveritem_t server);
public delegate void ServerFailedToRespond();
}
I assume that OnServerResponded and OnServerFailedToRespond are functions you can modify. You can use a TaskCompletionSource<bool> and await it's task. Something like this.
TaskCompletionSource<bool> pingSucceed;
//Not sure about the firm of your callback functions, just a void function for this example
void OnServerResponded()
{
//Do any task you need to do
//..
if(pingSucceed != null)
pingSucceed.TrySetResult(true);
}
void OnServerFailedToRespond()
{
//Do any task you need to do
//..
if(pingSucceed != null)
pingSucceed.TrySetResult(false);
}
//Now the call
async Task TheFunctionThatPingsTheServer()
{
//Do any task you need to do prior to the ping
//..
pingSucceed = new TaskCompletionSource<bool>();
m_ServerQuery = SteamMatchmakingServers.PingServer((uint)ip, port, m_PingResponse);
var succeed = await pingSucceed.Task;
pingSucceed.Dispose();
pingSucceed = null;
//Here succeed will be true if server answered, else false.
}
Beware with this, if OnServerResponded and/or OnServerFailedToRespond run in different threads then you must protect all the accesses to pingSucceed by locking an object to avoid race conditions.

Chaining tasks with delays

I have a need to keep track of a task and potentially queue up another task after some delay so the way I'm thinking of doing it looks something like this:
private Task lastTask;
public void DoSomeTask()
{
if (lastTask == null)
{
lastTask = Task.FromResult(false);
}
lastTask = lastTask.ContinueWith(t =>
{
// do some task
}).ContinueWith(t => Task.Delay(250).Wait());
}
My question is, if I do something like this, creating potentially long chains of tasks is will the older tasks be disposed or will they end up sticking around forever because the ContinueWith takes the last task as a parameter (so it's a closure). If so, how can I chain tasks while avoiding this problem?
Is there a better way to do this?
Task.Delay(250).Wait()
You know you're doing something wrong when you use Wait in code you're trying to make asynchronous. That's one wasted thread doing nothing.
The following would be much better:
lastTask = lastTask.ContinueWith(t =>
{
// do some task
}).ContinueWith(t => Task.Delay(250)).Unwrap();
ContinueWith returns a Task<Task>, and the Unwrap call turns that into a Task which will complete when the inner task does.
Now, to answer your question, let's take a look at what the compiler generates:
public void DoSomeTask()
{
if (this.lastTask == null)
this.lastTask = (Task) Task.FromResult<bool>(false);
// ISSUE: method pointer
// ISSUE: method pointer
this.lastTask = this.lastTask
.ContinueWith(
Program.<>c.<>9__2_0
?? (Program.<>c.<>9__2_0 = new Action<Task>((object) Program.<>c.<>9, __methodptr(<DoSomeTask>b__2_0))))
.ContinueWith<Task>(
Program.<>c.<>9__2_1
?? (Program.<>c.<>9__2_1 = new Func<Task, Task>((object) Program.<>c.<>9, __methodptr(<DoSomeTask>b__2_1))))
.Unwrap();
}
[CompilerGenerated]
[Serializable]
private sealed class <>c
{
public static readonly Program.<>c <>9;
public static Action<Task> <>9__2_0;
public static Func<Task, Task> <>9__2_1;
static <>c()
{
Program.<>c.<>9 = new Program.<>c();
}
public <>c()
{
base.\u002Ector();
}
internal void <DoSomeTask>b__2_0(Task t)
{
}
internal Task <DoSomeTask>b__2_1(Task t)
{
return Task.Delay(250);
}
}
This was decompiled with dotPeek in "show me all the guts" mode.
Look at this part:
.ContinueWith<Task>(
Program.<>c.<>9__2_1
?? (Program.<>c.<>9__2_1 = new Func<Task, Task>((object) Program.<>c.<>9, __methodptr(<DoSomeTask>b__2_1))))
The ContinueWith function is given a singleton delegate. So, there's no closing over any variable there.
Now, there's this function:
internal Task <DoSomeTask>b__2_1(Task t)
{
return Task.Delay(250);
}
The t here is a reference to the previous task. Notice something? It's never used. The JIT will mark this local as being unreachable, and the GC will be able to clean it. With optimizations enabled, the JIT will aggressively mark locals that are eligible for collection, even to the point that an instance method can be executing while the instance is being collected by the GC, if said instance method doesn't reference this in the code left to execute.
Now, one last thing, there's the m_parent field in the Task class, which is not good for your scenario. But as long as you're not using TaskCreationOptions.AttachedToParent you should be fine. You could always add the DenyChildAttach flag for extra safety and self-documentation.
Here's the function which deals with that:
internal static Task InternalCurrentIfAttached(TaskCreationOptions creationOptions)
{
return (creationOptions & TaskCreationOptions.AttachedToParent) != 0 ? InternalCurrent : null;
}
So, you should be safe here. If you want to be sure, run a memory profiler on a long chain, and see for yourself.
if I do something like this, creating potentially long chains of tasks is will the older tasks be disposed
Tasks do not require explicit disposal, as they don't contain unmanaged resources.
will they end up sticking around forever because the ContinueWith takes the last task as a parameter (so it's a closure)
It's not a closure. A closure is an anonymous method using a variable from outside the scope of that anonymous method in its body. You're not doing that, so you're not closing over it. Each Task does however have a field where it keeps track of its parent, so the managed Task object will still be accessible if you're using this pattern.
Take a look at the source code of the ContinuationTaskFromTask class. It has the following code:
internal override void InnerInvoke()
{
// Get and null out the antecedent. This is crucial to avoid a memory
// leak with long chains of continuations.
var antecedent = m_antecedent;
Contract.Assert(antecedent != null,
"No antecedent was set for the ContinuationTaskFromTask.");
m_antecedent = null;
m_antecedent is the field that holds a reference to the antecedent ask. The developers here explicitly set it to null (after it is no longer needed) to make sure that there is no memory leak with long chains of continuations, which I guess is your concern.

Verify that task is being awaited

I have the following code which i'd like to test:
private Task _keepAliveTask; // get's assigned by object initializer
public async Task EndSession()
{
_cancellationTokenSource.Cancel(); // cancels the _keepAliveTask
await _logOutCommand.LogOutIfPossible();
await _keepAliveTask;
}
It is important that the EndSession Task only ends once the `_keepAliveTask' ended. However, I'm struggling to find a way to test it reliably.
Question: How do i unit test the EndSession method and verify that the Task returned by EndSession awaits the _keepAliveTask.
For demonstration purposes, the unit test could look like that:
public async Task EndSession_MustWaitForKeepAliveTaskToEnd()
{
var keepAliveTask = new Mock<Task>();
// for simplicity sake i slightly differ from the other examples
// by passing the task as method parameter
await EndSession(keepAliveTask);
keepAliveTask.VerifyAwaited(); // this is what i want to achieve
}
Further criterias:
- reliable test (always passes when implementation is correct, always fails when implementation is wrong)
- cannot take longer than a few milliseconds (it's a unit test, after all).
I have already taken several alternatives into considerations which i'm documenting below:
non-async method
If there wouldn't be the call to _logOutCommand.LogOutIfPossible() it would be quite simple: i'd just remove the async and return _keepAliveTask instead of awaiting it:
public Task EndSession()
{
_cancellationTokenSource.Cancel();
return _keepAliveTask;
}
The unit test would look (simplified):
public void EndSession_MustWaitForKeepAliveTaskToEnd()
{
var keepAliveTask = new Mock<Task>();
// for simplicity sake i slightly differ from the other examples
// by passing the task as method parameter
Task returnedTask = EndSession(keepAliveTask);
returnedTask.Should().be(keepAliveTask);
}
However, there's two arguments against this:
i have multiple task which need awaiting (i'm considering Task.WhenAll further down)
doing so only moves the responsibility to await the task to the caller of EndSession. Still will have to test it there.
non-async method, sync over async
Of course, I could do something similar:
public Task EndSession()
{
_cancellationTokenSource.Cancel(); // cancels the _keepAliveTask
_logOutCommand.LogOutIfPossible().Wait();
return _keepAliveTask;
}
But that is a no-go (sync over async). Plus it still has the problems of the previous approach.
non-async method using Task.WhenAll(...)
Is a (valid) performance improvement but introduces more complexity:
- difficult to get right without hiding a second exception (when both fail)
- allows parallel execution
Since performance isn't key here i'd like to avoid the extra complexity. Also, previously mentioned issue that it just moves the (verification) problem to the caller of the EndSession method applies here, too.
observing effects instead of verifying calls
Now of course instead of "unit" testing method calls etc. I could always observe effects. Which is: As long as _keepAliveTask hasn't ended the EndSession Task mustn't end either. But since I can't wait indefinite one has to settle for a timeout. The tests should be fast so a timeout like 5 seconds is a no go. So what I've done is:
[Test]
public void EndSession_MustWaitForKeepAliveTaskToEnd()
{
var keepAlive = new TaskCompletionSource<bool>();
_cancelableLoopingTaskFactory
.Setup(x => x.Start(It.IsAny<ICancelableLoopStep>(), It.IsAny<CancellationToken>()))
.Returns(keepAlive.Task);
_testee.StartSendingKeepAlive();
_testee.EndSession()
.Wait(TimeSpan.FromMilliseconds(20))
.Should().BeFalse();
}
But I really really dislike this approach:
hard to understand
unreliable
or - when it's quite reliable - it takes a long time (which unit tests shouldn't).
If all you want is to verify that EndSession is awaiting _keepAliveTask (and you really have full control over _keepAliveTask) then you can create your own awaitable type instead of Task the signals when it's awaited and check that:
public class MyAwaitable
{
public bool IsAwaited;
public MyAwaiter GetAwaiter()
{
return new MyAwaiter(this);
}
}
public class MyAwaiter
{
private readonly MyAwaitable _awaitable;
public MyAwaiter(MyAwaitable awaitable)
{
_awaitable = awaitable;
}
public bool IsCompleted
{
get { return false; }
}
public void GetResult() {}
public void OnCompleted(Action continuation)
{
_awaitable.IsAwaited = true;
}
}
Since all you need to await something is that has a GetAwaiter method that returns something with IsCompleted, OnCompleted and GetResult you can use the dummy awaitable to make sure _keepAliveTask is being awaited:
_keepAliveTask = new MyAwaitable();
EndSession();
_keepAliveTask.IsAwaited.Should().BeTrue();
If you use some mocking framework you can instead make Task's GetAwaiter return our MyAwaiter.
Use TaskCompletionSource and set its result at a known time.
Verify that before setting the result, the await on EndSession hasn't completed.
Verify that after setting the result, the await on EndSession has completed.
A simplified version could look like the following (using nunit):
[Test]
public async Task VerifyTask()
{
var tcs = new TaskCompletionSource<bool>();
var keepAliveTask = tcs.Task;
// verify pre-condition
Assert.IsFalse(keepAliveTask.IsCompleted);
var waitTask = Task.Run(async () => await keepAliveTask);
tcs.SetResult(true);
await waitTask;
// verify keepAliveTask has finished, and as such has been awaited
Assert.IsTrue(keepAliveTask.IsCompleted);
Assert.IsTrue(waitTask.IsCompleted); // not needed, but to make a point
}
You can also add a short delay at the waitTask to ensure any synchronous execution would be faster, something like:
var waitTask = Task.Run(async () =>
{
await Task.Delay(1);
await keepAliveTask;
});
And if you don't trust your unit test framework to deal correctly with async, you can set a completed flag as part of the waitTask, and check for that in the end. Something like:
bool completed = false;
var waitTask = Task.Run(async () =>
{
await Task.Delay(1);
await keepAliveTask;
completed = true;
});
// { .... }
// at the end of the method
Assert.IsTrue(completed);

Use a Task to avoid multiple calls to expensive operation and to cache its result

I have an async method that fetches some data from a database. This operation is fairly expensive, and takes a long time to complete. As a result, I'd like to cache the method's return value. However, it's possible that the async method will be called multiple times before its initial execution has a chance to return and save its result to the cache, resulting in multiple calls to this expensive operation.
To avoid this, I'm currently reusing a Task, like so:
public class DataAccess
{
private Task<MyData> _getDataTask;
public async Task<MyData> GetDataAsync()
{
if (_getDataTask == null)
{
_getDataTask = Task.Run(() => synchronousDataAccessMethod());
}
return await _getDataTask;
}
}
My thought is that the initial call to GetDataAsync will kick off the synchronousDataAccessMethod method in a Task, and any subsequent calls to this method before the Task has completed will simply await the already running Task, automatically avoiding calling synchronousDataAccessMethod more than once. Calls made to GetDataAsync after the private Task has completed will cause the Task to be awaited, which will immediately return the data from its initial execution.
This seems to be working, but I'm having some strange performance issues that I suspect may be tied to this approach. Specifically, awaiting _getDataTask after it has completed takes several seconds (and locks the UI thread), even though the synchronousDataAccessMethod call is not called.
Am I misusing async/await? Is there a hidden gotcha that I'm not seeing? Is there a better way to accomplish the desired behavior?
EDIT
Here's how I call this method:
var result = (await myDataAccessObject.GetDataAsync()).ToList();
Maybe it has something to do with the fact that the result is not immediately enumerated?
If you want to await it further up the call stack, I think you want this:
public class DataAccess
{
private Task<MyData> _getDataTask;
private readonly object lockObj = new Object();
public async Task<MyData> GetDataAsync()
{
lock(lockObj)
{
if (_getDataTask == null)
{
_getDataTask = Task.Run(() => synchronousDataAccessMethod());
}
}
return await _getDataTask;
}
}
Your original code has the potential for this happening:
Thread 1 sees that _getDataTask == null, and begins constructing the task
Thread 2 sees that _getDataTask == null, and begins constructing the task
Thread 1 finishes constructing the task, which starts, and Thread 1 waits on that task
Thread 2 finishes constructing a task, which starts, and Thread 2 waits on that task
You end up with two instances of the task running.
Use the lock function to prevent multiple calls to the database query section. Lock will make it thread safe so that once it has been cached all the other calls will use it instead of running to the database for fulfillment.
lock(StaticObject) // Create a static object so there is only one value defined for this routine
{
if(_getDataTask == null)
{
// Get data code here
}
return _getDataTask
}
Please rewrite your function as:
public Task<MyData> GetDataAsync()
{
if (_getDataTask == null)
{
_getDataTask = Task.Run(() => synchronousDataAccessMethod());
}
return _getDataTask;
}
This should not change at all the things that can be done with this function - you can still await on the returned task!
Please tell me if that changes anything.
Bit late to answer this but there is an open source library called LazyCache that will do this for you in two lines of code and it was recently updated to handle caching Tasks for just this sort of situation. It is also available on nuget.
Example:
Func<Task<List<MyData>>> cacheableAsyncFunc = () => myDataAccessObject.GetDataAsync();
var cachedData = await cache.GetOrAddAsync("myDataAccessObject.GetData", cacheableAsyncFunc);
return cachedData;
// Or instead just do it all in one line if you prefer
// return await cache.GetOrAddAsync("myDataAccessObject.GetData", myDataAccessObject.GetDataAsync);
}
It has built in locking by default so the cacheable method will only execute once per cache miss, and it uses a lamda so you can do "get or add" in one go. It defaults to 20 minutes sliding expiration but you can set whatever caching policy you like on it.
More info on caching tasks is in the api docs and you may find the sample app to demo caching tasks useful.
(Disclaimer: I am the author of LazyCache)

Unenviable duplication of code in C#

I have the following simple method in C#:
private static void ExtendTaskInternal<U>(
ref U task_to_update, U replace, Action a) where U : Task
{
var current = Interlocked.Exchange(ref task_to_update, replace);
if (current == null)
Task.Run(a);
else
current.AppendAction(a);
}
This is used for the following methods:
//A Task can only run once. But sometimes we wanted to have a reference to some
//tasks that can be restarted. Of cause, in this case "restart" a task means
//replace the reference with a new one. To safely do so we have to ensure a
//lot of things:
//
// * Would the referee be null?
// * Is it still running?
// * The replacement of the task must be atomic
//
//This method can help solving the above issues. If `task_to_update` is null,
//a new Task will be created to replace it. If it is already there, a new Task
//will be created as its continuation, which will only run when the previous
//one finishes.
//
//This is looks like a async mutex, since if you assume `ExtendTask` is the only
//function in your code that updates `task_to_update`, the delegates you pass to
//it runs sequentially. But the difference is that since you have a reference to
//a Task, you can attach continuations that receive notification of lock
//releases.
public static Task<T> ExtendTask<T>(ref Task<T> task_to_update, Func<T> func)
{
var next_ts = new TaskCompletionSource<T>();
ExtendTaskInternal(ref task_to_update, next_ts.Task,
() => next_ts.SetResult(func()));
return next_ts.Task;
}
If you want to do something but only after something else have already been done, this is useful.
Now, this version can only used to replace a Task<T>, not a Task since ref variables are invariant. So if you want it to work for Task as well you have to duplicate the code:
public static Task<T> ExtendTask<T>(ref Task task_to_update, Func<T> func)
{
var next_ts = new TaskCompletionSource<T>();
ExtendTaskInternal(ref task_to_update, next_ts.Task,
() => next_ts.SetResult(func()));
return next_ts.Task;
}
And so you can implement another version that works on Actions.
public static Task ExtendTask(ref Task task_to_update, Action a)
{
return ExtendTask(ref task_to_update, () =>
{
a();
return true;
});
}
So far so good. But I don't like the first and the second version of the ExtendTask, since the body looks exactly the same.
Are there any way to eliminate the duplication?
Background
People ask why not use ContinueWith.
First, notice that AppendAction is just a wrapper function (from Microsoft.VisualStudio.Threading) of ContinueWith so this code is already using it indirectly.
Second, What I did differently here is that I have a reference to update, so this is another wrapper function to ContinueWith, the purpose of those functions is to make it easier to use in some scenarios.
I provide the following concrete example (untested) to illustrate the usage of those methods.
public class Cat {
private Task miuTask = null;
//you have to finish a miu to start another...
private void DoMiu(){
//... do what ever required to "miu".
}
public Task MiuAsync(){
return MyTaskExtension.ExtendTask(ref miuTask, DoMiu);
}
public void RegisterMiuListener(Action whenMiued){
var current = miuTask;
if(current==null) current = TplExtensions.CompletedTask();
current.AppendAction(whenMiued);
}
}

Categories