Reputation: 930
I'm developing an online game server that uses C# for its NPC scripts. To get stateless NPCs I'm using enumerators, yielding when I have to wait for a response from the client, and calling MoveNext once I got it.
Talk()
Msg("test");
Select("Yes", "No");
yield return true;
Msg(Response);
(Simplified example, the yielding is a little more complex in reality.)
This is working fine, but async/wait would make make it cleaner, easier, and more flexible.
Talk()
Msg("test");
Msg(await Select("Yes", "No"));
Playing around with async/wait I've found my only problem to be that the scripts aren't as simple as sending some messages. When calling other functions from the main Talk function I have to await them, since the execution wouldn't stop otherwise. The other functions could also call even more, creating an unknown amount of awaits.
Talk()
Msg("test");
await OtherTalk();
Msg("end"); // would be called right away
OtherTalk()
Msg("test2");
Msg(await Select("Yes", "No"));
If I close the NPC inside such a "sub talk" method I'd potentially leave quite a few tasks hanging in the air, because I wouldn't return up the chain. NPC closed, no more responses, tasks keep waiting.
The solution to this problem would be going the chain back up, with explicit checks after the await of such a function, to check if the NPC was closed somewhere down the line. But I want them to be as simple and straight forward as possible, and frankly, having an if after each call of a function would be too tedious for my taste. The server software will also be used by newbies, who might forget such a check, which could lead to problems, depending on what the script does.
My actual question now is if someone can think of a way to cancel the Tasks, without explicit checks in the scripts themselves.
Upvotes: 0
Views: 214
Reputation: 888117
The TPL has built-in cancellation features.
Make all of your functions accept a CanellationToken
, and pass the token along when calling every async function.
Every now and then, inside the functions, call token.ThrowIfCancellationRequested()
, and the entire async callchain will abort, until you handle the cancellation.
Upvotes: 4
Reputation: 171246
You could install a custom SynchronizationContext
that you can cancel. On cancellation it just would stop calling continuations. The async
methods would effectively stop immediately after the next await
. If nothing is holding on to those tasks they will be GC'ed and just go away.
Be aware that this way even finally
blocks would not execute. using
cleanups would not take place etc. Your code must be hardened to be safe under these circumstances.
Upvotes: 1
Reputation: 14332
This is a short answer.
Task cancellation must always be cooperative cancellation if you want to abort safely.
Upvotes: 0