Skip to content

Commit 0e60b87

Browse files
committed
section for blocking in callbacks
1 parent 616c0ec commit 0e60b87

File tree

1 file changed

+104
-1
lines changed

1 file changed

+104
-1
lines changed

README.md

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,110 @@ var result = await client.RpcAsync("method", data);
110110
// It waits for disconnect to complete before releasing resources
111111
```
112112

113-
### Subscriptions
113+
## Event Handlers and Callbacks
114+
115+
### ⚠️ Critical: Avoiding Deadlocks
116+
117+
The SDK uses event handlers extensively for real-time notifications. To maintain high performance and avoid deadlocks, follow these guidelines:
118+
119+
**✅ DO: Use async/await for I/O operations**
120+
121+
```csharp
122+
client.Connected += async (sender, e) =>
123+
{
124+
Console.WriteLine($"Connected: {e.ClientId}");
125+
126+
// ✅ Safe: Use await for SDK async methods
127+
var sub = client.NewSubscription("chat");
128+
sub.Subscribe();
129+
await sub.ReadyAsync();
130+
await sub.PublishAsync(Encoding.UTF8.GetBytes("Hello!"));
131+
};
132+
133+
subscription.Publication += async (sender, e) =>
134+
{
135+
var message = Encoding.UTF8.GetString(e.Data.Span);
136+
137+
// ✅ Safe: Async I/O operations with await
138+
await File.WriteAllTextAsync("messages.txt", message);
139+
await httpClient.PostAsJsonAsync("/api/messages", new { message });
140+
};
141+
```
142+
143+
**❌ DON'T: Block on SDK async methods**
144+
145+
```csharp
146+
client.Connected += (sender, e) =>
147+
{
148+
// ❌ DEADLOCK! Never use .Wait(), .Result, or .GetAwaiter().GetResult()
149+
sub.PublishAsync(data).Wait(); // DEADLOCK!
150+
var result = client.RpcAsync("method", data).Result; // DEADLOCK!
151+
152+
// ❌ DEADLOCK! Even if you try to be clever
153+
Task.Run(() => sub.PublishAsync(data)).Wait(); // Still DEADLOCK!
154+
};
155+
```
156+
157+
**Why does this deadlock?**
158+
159+
The SDK dispatches events on the thread pool but uses internal synchronization to maintain message order and state consistency. When you block an event handler waiting for an SDK async method to complete, you create a circular wait:
160+
161+
1. Event handler blocks waiting for `PublishAsync()` to complete
162+
2. `PublishAsync()` needs to acquire internal locks held by the event dispatcher
163+
3. Event dispatcher can't release locks until your handler completes
164+
4. **Deadlock!** ⚠️
165+
166+
**⚠️ Blocking on Non-SDK Operations**
167+
168+
```csharp
169+
subscription.Publication += (sender, e) =>
170+
{
171+
// ⚠️ Safe from deadlock, but blocks a thread pool thread (impacts performance)
172+
File.WriteAllText("messages.txt", message); // Blocks thread pool
173+
database.Insert(message); // Blocks thread pool
174+
175+
// ✅ Better: Use async/await
176+
// await File.WriteAllTextAsync("messages.txt", message);
177+
};
178+
```
179+
180+
Blocking on non-SDK operations (file I/O, database calls, HTTP requests to other services) won't deadlock, but it ties up a thread pool thread, reducing overall application performance. Use `async`/`await` whenever possible.
181+
182+
**Exception Handling in Async Event Handlers**
183+
184+
Exceptions in `async void` event handlers **cannot be caught by the SDK** and will crash your application if unhandled:
185+
186+
```csharp
187+
// ❌ BAD: Unhandled exceptions will crash your app
188+
subscription.Publication += async (sender, e) =>
189+
{
190+
await riskyOperation(); // If this throws, app crashes!
191+
};
192+
193+
// ✅ GOOD: Always wrap async event handlers in try-catch
194+
subscription.Publication += async (sender, e) =>
195+
{
196+
try
197+
{
198+
await riskyOperation();
199+
}
200+
catch (Exception ex)
201+
{
202+
Console.WriteLine($"Error processing publication: {ex.Message}");
203+
// Log, report to error tracking service, etc.
204+
}
205+
};
206+
```
207+
208+
**Best Practices Summary**
209+
210+
1. ✅ Use `async void` for event handlers that need to perform async I/O
211+
2. ✅ Always `await` SDK async methods (never block with `.Wait()` or `.Result`)
212+
3. ✅ Always wrap `async void` handlers in `try-catch` to prevent crashes
213+
4. ✅ Keep handlers fast - offload heavy CPU work to background tasks if needed
214+
5. ⚠️ Avoid blocking operations - use async alternatives when available
215+
216+
## Subscriptions
114217

115218
```csharp
116219
// Create subscription, may throw CentrifugeDuplicateSubscriptionException

0 commit comments

Comments
 (0)