0

I have a task adapter

public static ServiceReturnObject<T> CheckServiceResult<T>(Task<T> functionTask)
{
    ServiceReturnObject<T> serviceReturnObject = new ServiceReturnObject<T>();
    var result = functionTask.GetAwaiter().GetResult();
...
}

which i can call with

CheckServiceResult(GetSomaAlgorithmResultAsync(header, AlgoNo));

where GetSomaAlgorithmResultAsync is a function and header and AlgoNo are parameters I pass.

This works. No problem.

functionTask.GetAwaiter().GetResult() works fine with parameters but I cant get the parameters myself. This is here just as a proof of concept that I can run the funcTask with parameters correctly. Dont stay on that.

How can I access parameters (header or AlgoNo) from within CheckServiceResult?

pi511
  • 27
  • 6
  • `This works. No problem.` actually, that's a very serious bug that leads to wasted threads and CPU at best, deadlocks and even cascading crashes at worst. `.GetResult();` blocks the calling thread while waiting for the asynchronous operation to complete, eating up a CPU core. That's almost *never* a good idea, unless the task had already completed. `serviceReturnObject` is never used. – Panagiotis Kanavos Jan 16 '23 at 15:10
  • 1
    CheckServiceResult takes a Task not a Function. And as far as i know the Task does not have the info about what Function might have created it. – Ralf Jan 16 '23 at 15:10
  • The reason this can cause cascading server crashes is that task are *not* meant to be blocked. In general, blocking is supposed to be short lived. To avoid evicting a thread from the CPU and having to reschedule it, .NET starts each blocking operation by spinwaiting, which uses 100% of a core. Only once the spinwait ends will .NET allow the thread to be evicted. If a moderately busy application blocks like this, all available CPU cores will end up doing nothing but heating the CPU. – Panagiotis Kanavos Jan 16 '23 at 15:14
  • At some point IIS, Kubernetes or whatever monitors the server will recycle the application and redirect the traffic to another server in the farm ... which could proceed to livelock itself – Panagiotis Kanavos Jan 16 '23 at 15:14
  • Thank you, the site has been working for three years now, and im not the developer, i just want to cache the result based on the parameters. ServiceReturnObject is used down below. I wonder how functionTask can run with passed parameters but i cant see them. – pi511 Jan 16 '23 at 15:49

1 Answers1

1

This works. No problem.

Until there are. You should not block on async code. There are numerous problem which can happen - deadlocks, resource wasting, etc.

How can I access parameters from within CheckServiceResult?

You can't (at least in relatively easy way). There are two approaches which require signature and logic changes (switching to factory approach):

  1. Use expression trees, so you change from Task<T> functionTask parameter to Expression<Func<Task<T>>> and then process the expression tree to get invocation parameters:
public static ServiceReturnObject<T> CheckServiceResult<T>(Expression<Func<Task<T>>> expr)
{
   var task = expression.Compile()(); // compile and start task
   // process expression
   
}

Processing the expression can be quite cumbersome (you will need to process closures) and there is performance hit involved but you will be able to keep the signature.

  1. Create a lot of CheckServiceResult overloads for different number of parameters (maybe using source generators):
public static ServiceReturnObject<TResult> CheckServiceResult<T, TResult>(Func<T, Task<TResult>> expr, T p)
{
   var task = expr()(p);
   
}
public static ServiceReturnObject<TResult> CheckServiceResult<T, T1, TResult>(Func<T, T1, Task<TResult>> expr, T p, T1 p1)
...

Which makes the method simpler and has less performance impact, but usage changes to: CheckServiceResult(GetSomaAlgorithmResultAsync, header, AlgoNo) (leveraging method group syntax).

Guru Stron
  • 102,774
  • 10
  • 95
  • 132