Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Add the following nuget package to you project: https://www.nuget.org/packages/E
| [EPC31](https://github.com/SergeyTeplyakov/ErrorProne.NET/tree/master/docs/Rules/EPC31.md) | Do not return null for Task-like types |
| [EPC32](https://github.com/SergeyTeplyakov/ErrorProne.NET/tree/master/docs/Rules/EPC32.md) | TaskCompletionSource should use RunContinuationsAsynchronously |
| [EPC33](https://github.com/SergeyTeplyakov/ErrorProne.NET/tree/master/docs/Rules/EPC33.md) | Do not use Thread.Sleep in async methods |
| [EPC35](https://github.com/SergeyTeplyakov/ErrorProne.NET/tree/master/docs/Rules/EPC35.md) | Do not block unnecessarily in async methods |

### Generic Bugs and Code Smells

Expand Down
106 changes: 106 additions & 0 deletions docs/Rules/EPC35.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# EPC35 - Do not block unnecessarily in async methods

This analyzer detects synchronous blocking operations inside async methods that can lead to deadlocks and poor performance.

## Description

The analyzer warns when you use blocking operations like `.Wait()`, `.Result`, or `.GetAwaiter().GetResult()` on Task-like types inside async methods. These blocking operations defeat the purpose of async programming and can cause deadlocks, especially in UI applications or when there's a limited synchronization context.

## Code that triggers the analyzer

```csharp
public async Task ProcessAsync()
{
// Using .Wait() blocks the thread
SomeAsyncMethod().Wait(); // ❌ EPC35

// Using .Result property blocks the thread
var result = SomeAsyncMethod().Result; // ❌ EPC35

// Using GetAwaiter().GetResult() blocks the thread
var data = GetDataAsync().GetAwaiter().GetResult(); // ❌ EPC35

// More async work
await AnotherAsyncMethod();
}
```

## How to fix

Use `await` instead of blocking operations:

```csharp
public async Task ProcessAsync()
{
// Use await instead of .Wait()
await SomeAsyncMethod(); // ✅ Correct

// Use await instead of .Result
var result = await SomeAsyncMethod(); // ✅ Correct

// Use await instead of GetAwaiter().GetResult()
var data = await GetDataAsync(); // ✅ Correct

// More async work
await AnotherAsyncMethod();
}
```

## Why this is important

1. **Deadlock Prevention**: Blocking on async operations can cause deadlocks, especially when there's a synchronization context (like in UI applications)
2. **Performance**: Blocking ties up thread pool threads unnecessarily
3. **Scalability**: Proper async/await allows better resource utilization
4. **Consistency**: Mixing blocking and async patterns makes code harder to reason about

## Examples of problematic patterns

```csharp
// UI event handler - high risk of deadlock
private void Button_Click(object sender, EventArgs e)
{
ProcessDataAsync().Wait(); // ❌ Very dangerous in UI context
}

// ASP.NET Controller - can cause thread pool starvation
public ActionResult GetData()
{
var result = FetchDataAsync().Result; // ❌ Blocks request thread
return Json(result);
}

// Library method - blocks caller unnecessarily
public string GetConfiguration()
{
return LoadConfigAsync().GetAwaiter().GetResult(); // ❌ Forces synchronous behavior
}
```

## Correct async patterns

```csharp
// UI event handler - make it async
private async void Button_Click(object sender, EventArgs e)
{
await ProcessDataAsync(); // ✅ Proper async UI pattern
}

// ASP.NET Controller - return Task
public async Task<ActionResult> GetData()
{
var result = await FetchDataAsync(); // ✅ Truly async request handling
return Json(result);
}

// Library method - keep it async
public async Task<string> GetConfigurationAsync()
{
return await LoadConfigAsync(); // ✅ Maintains async all the way
}
```

## Related rules

- **EPC27**: Avoid async void methods (except for event handlers)
- **EPC31**: Do not return null for Task-like types
- **EPC33**: Do not use Thread.Sleep in async methods
Loading