Commit 3ac074b
authored
fix: prevent sync-over-async deadlock when async configure callback is used in TypeScript AppHost (#26)
* fix: prevent sync-over-async deadlock in IOptions configure callback
The configure delegate passed to AddAspireC4 is an ATS-generated proxy for
a TypeScript async callback. The proxy calls .GetAwaiter().GetResult()
internally to bridge the sync/async boundary.
Previously, configure?.Invoke(opts) was called inside the lazy
IOptions.Configure callback. When DistributedApplication.RunAsync accessed
IOptions<AspireC4DiagramOptions>.Value, that callback ran on StreamJsonRpc's
NonConcurrentSynchronizationContext. The .GetResult() call blocked the
context while waiting for TypeScript's incoming setter responses, which
themselves needed the same blocked context to be dispatched. Classic
sync-over-async deadlock.
Fix: eagerly evaluate configure on the background thread that AddAspireC4
runs on (guaranteed by RunSyncOnBackgroundThread = true). Configuration
values are bound first (so the callback sees and can intentionally
override/clear them), then configure?.Invoke is called, and the resulting
fully-materialised snapshot is captured. The lazy IOptions.Configure
callback uses CopyTo to apply that snapshot, never touching the ATS proxy.
Add AspireC4DiagramOptions.CopyTo(target) - copies all properties including
collection and dictionary types without sharing mutable references.
Add regression tests covering:
- CopyTo scalar, nullable, collection, and dictionary properties
- CopyTo preserving explicit null/empty overrides (code wins over defaults)
- IOptions resolution applying configure callback values end-to-end
* docs: add troubleshooting guide and diagnostic logging for configure callback deadlock
Add two structured log entries to IAspireC4LifecycleHookTelemetry:
- ApplyingDiagramOptionsSnapshot (Debug) — emitted immediately before IOptions.Value
is resolved so a deadlock in this area leaves a clear breadcrumb in the log
- DiagramOptionsSnapshotApplied (Debug) — emitted after successful resolution with
key option values for traceability
Add a Troubleshooting section to the README covering the sync-over-async deadlock
that caused aspire start to hang silently for 60 s. Includes symptom, cause, dotnet-dump
diagnosis steps, and the upstream issue reference (microsoft/aspire#17487).
* revert: remove README troubleshooting section
* fix: address review feedback on deadlock fix
- ApplyDelta: evaluate callback against fresh defaults (no config binding),
restore BindConfiguration lazily, apply only callback-changed properties on
top of config — preserves late-added configuration values and correct
config < code precedence (microsoft/aspire#17487)
- CopyTo/ApplyDelta: preserve source dictionary comparer instead of
hardcoding OrdinalIgnoreCase for ImageAliases
- Telemetry: log only the output directory name (not the full path) to
avoid leaking sensitive filesystem paths in logs
- Comment: scope the RunSyncOnBackgroundThread guarantee to the ATS/TypeScript
export path; C# callers are not affected
- Tests: add AddAspireC4_IOptions_LateAddedConfigIsReflected,
AddAspireC4_IOptions_CallbackWinsOverConfig, and six ApplyDelta unit tests
covering the scalar/collection/nullable/ExcludedResourceTypes delta semantics1 parent 392c8e6 commit 3ac074b
6 files changed
Lines changed: 533 additions & 13 deletions
File tree
- src
- src/AspireC4
- Extensions/Aspire/Hosting
- Lifecycle
- tests/AspireC4.UnitTests
- Extensions/Aspire/Hosting
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
354 | 354 | | |
355 | 355 | | |
356 | 356 | | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
357 | 524 | | |
Lines changed: 38 additions & 13 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
54 | 77 | | |
55 | 78 | | |
56 | 79 | | |
57 | 80 | | |
58 | 81 | | |
59 | | - | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
60 | 86 | | |
61 | 87 | | |
62 | 88 | | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
| 89 | + | |
67 | 90 | | |
68 | | - | |
| 91 | + | |
69 | 92 | | |
70 | | - | |
71 | | - | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
72 | 97 | | |
73 | 98 | | |
74 | 99 | | |
75 | 100 | | |
76 | 101 | | |
77 | | - | |
| 102 | + | |
78 | 103 | | |
79 | 104 | | |
80 | 105 | | |
| |||
91 | 116 | | |
92 | 117 | | |
93 | 118 | | |
94 | | - | |
| 119 | + | |
95 | 120 | | |
96 | 121 | | |
97 | 122 | | |
| |||
156 | 181 | | |
157 | 182 | | |
158 | 183 | | |
159 | | - | |
| 184 | + | |
160 | 185 | | |
161 | 186 | | |
162 | | - | |
| 187 | + | |
163 | 188 | | |
164 | 189 | | |
165 | 190 | | |
| |||
190 | 215 | | |
191 | 216 | | |
192 | 217 | | |
193 | | - | |
| 218 | + | |
194 | 219 | | |
195 | 220 | | |
196 | 221 | | |
| |||
Lines changed: 12 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
13 | 19 | | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
14 | 26 | | |
15 | 27 | | |
16 | 28 | | |
| |||
Lines changed: 6 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
43 | 49 | | |
0 commit comments