Table of Contents

CHT002: ValueTask blocked with GetAwaiter().GetResult()

Cause

GetAwaiter().GetResult() is called on a ValueTask or ValueTask<T>.

Rule Description

Calling GetAwaiter().GetResult() on a ValueTask is undefined behavior when the ValueTask is backed by an IValueTaskSource. This pattern:

  1. May cause deadlocks when called on a non-completed ValueTask
  2. Is undefined behavior for IValueTaskSource-backed ValueTasks
  3. Consumes the ValueTask, preventing further usage

The pooled primitives in CryptoHives.Foundation.Threading use IValueTaskSource for their ValueTask implementations, making this pattern particularly dangerous.

How to Fix

Convert to async code:

// Before
valueTask.GetAwaiter().GetResult();

// After
await valueTask;

Option 2: Preserve the ValueTask first

If blocking is absolutely necessary (not recommended):

// Before
valueTask.GetAwaiter().GetResult();

// After
valueTask.Preserve().GetAwaiter().GetResult();

Option 3: Convert to Task first

If blocking is absolutely necessary (not recommended):

// Before
valueTask.GetAwaiter().GetResult();

// After
valueTask.AsTask().GetAwaiter().GetResult();

Only suppress if you can guarantee:

  1. The ValueTask is already completed (IsCompletedSuccessfully == true)
  2. The ValueTask is backed by a Task, not an IValueTaskSource
ValueTask vt = GetValueTask();
if (vt.IsCompletedSuccessfully)
{
#pragma warning disable CHT002
    vt.GetAwaiter().GetResult(); // Safe only because already completed
#pragma warning restore CHT002
}

Example

Violating Code

public void SyncMethod()
{
    ValueTask vt = asyncEvent.WaitAsync();
    vt.GetAwaiter().GetResult(); // CHT002
}

Fixed Code

// Option 1: Make method async
public async Task AsyncMethod()
{
    await asyncEvent.WaitAsync();
}

// Option 2: Convert to Task first (if blocking is necessary)
public void SyncMethod()
{
    asyncEvent.WaitAsync().AsTask().GetAwaiter().GetResult();
}

See Also