Commit dffd529
🤖 *This is an automated pull request from [Repo
Assist](https://github.com/fsprojects/FSharp.TypeProviders.SDK/actions/runs/23358988317).*
Fixes #481 — `InvalidOperationException` / `NullReferenceException` when
the F# compiler accesses type-provider types from multiple parallel
compilation threads.
## Root cause
PR #471 introduced lazy caches
(`ctorDefs`/`methDefs`/`fieldDefs`/`eventDefs`/`propDefs`/`nestedDefs`)
in `TargetTypeDefinition` so member-wrapper objects are allocated once
and reused. When the compiler invokes
`GetConstructors`/`GetMethods`/etc. from multiple threads on the same
type, several underlying caches that were never designed for concurrent
access are hit simultaneously:
| Site | Problem |
|---|---|
| `ILMethodDefs.getmap()` / `ILTypeDefs.getmap()` /
`ILExportedTypesAndForwarders.getmap()` | `mutable lmap = null` checked
without a lock. Thread A sets `lmap` to a new `Dictionary` and starts
filling it; Thread B sees the non-null value and reads from it while
Thread A is writing → `InvalidOperationException`. |
| `mkCacheInt32` / `mkCacheGeneric` (binary-reader row caches) | Same
unsynchronised `ref null` / `Dictionary` pattern across all 8 per-reader
caches. |
| `TxTable(T).Get` | `Dictionary(int,T)` written without any lock;
concurrent type-resolution calls (via cached
`MethodInfo`/`ConstructorInfo` → `txILTypeRef` → `txTable.Get`) from two
threads can collide. |
## Fix
* **`ILMethodDefs` / `ILTypeDefs` / `ILExportedTypesAndForwarders`** –
add a `syncObj` per instance; build `lmap` inside `lock syncObj` so the
dictionary is fully populated before any reader sees it. Subsequent
calls acquire the lock, see the already-set field, and return
immediately.
* **`mkCacheInt32` / `mkCacheGeneric`** – each closure now owns a
`syncObj` and wraps every `TryGetValue` + write pair in `lock`.
* **`TxTable(T)`** – backed by `ConcurrentDictionary(int, Lazy<T)>`.
`GetOrAdd` races safely; the `Lazy(T)` wrapper (using F#'s default
`ExecutionAndPublication` mode) ensures the factory is called **at most
once** per token, preserving the identity-equality guarantee that
`TxTable` provides.
## Test status
New regression test added: **`TargetTypeDefinition member-wrapper caches
are thread-safe under parallel access`** — 8 threads × 50 iterations,
each calling all six `GetXxx` methods on the same `TargetTypeDefinition`
concurrently.
```
Passed! - Failed: 0, Passed: 118, Skipped: 0, Total: 118 (net8.0)
```
All pre-existing tests continue to pass.
> Generated by [Repo
Assist](https://github.com/fsprojects/FSharp.TypeProviders.SDK/actions/runs/23358988317)
for issue #481 ·
[◷](https://github.com/search?q=repo%3Afsprojects%2FFSharp.TypeProviders.SDK+%22gh-aw-workflow-id%3A+repo-assist%22&type=pullrequests)
>
> To install this [agentic
workflow](https://github.com/githubnext/agentics/tree/d1d884596e62351dd652ae78465885dd32f0dd7d/workflows/repo-assist.md),
run
> ```
> gh aw add
githubnext/agentics@d1d8845
> ```
<!-- gh-aw-agentic-workflow: Repo Assist, engine: copilot, id:
23358988317, workflow_id: repo-assist, run:
https://github.com/fsprojects/FSharp.TypeProviders.SDK/actions/runs/23358988317
-->
<!-- gh-aw-workflow-id: repo-assist -->
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
Co-authored-by: Sergey Tihon <sergey.tihon@gmail.com>
1 parent 821de01 commit dffd529
2 files changed
Lines changed: 81 additions & 55 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2874 | 2874 | | |
2875 | 2875 | | |
2876 | 2876 | | |
2877 | | - | |
2878 | | - | |
2879 | | - | |
2880 | | - | |
2881 | | - | |
2882 | | - | |
2883 | | - | |
2884 | | - | |
2885 | | - | |
2886 | | - | |
| 2877 | + | |
| 2878 | + | |
| 2879 | + | |
| 2880 | + | |
| 2881 | + | |
| 2882 | + | |
| 2883 | + | |
| 2884 | + | |
| 2885 | + | |
2887 | 2886 | | |
2888 | 2887 | | |
2889 | 2888 | | |
| |||
3097 | 3096 | | |
3098 | 3097 | | |
3099 | 3098 | | |
3100 | | - | |
3101 | | - | |
3102 | | - | |
3103 | | - | |
3104 | | - | |
3105 | | - | |
3106 | | - | |
3107 | | - | |
| 3099 | + | |
| 3100 | + | |
| 3101 | + | |
| 3102 | + | |
| 3103 | + | |
| 3104 | + | |
3108 | 3105 | | |
3109 | 3106 | | |
3110 | 3107 | | |
| |||
3142 | 3139 | | |
3143 | 3140 | | |
3144 | 3141 | | |
3145 | | - | |
3146 | | - | |
3147 | | - | |
3148 | | - | |
3149 | | - | |
3150 | | - | |
3151 | | - | |
3152 | | - | |
| 3142 | + | |
| 3143 | + | |
| 3144 | + | |
| 3145 | + | |
| 3146 | + | |
| 3147 | + | |
3153 | 3148 | | |
3154 | 3149 | | |
3155 | 3150 | | |
| |||
4579 | 4574 | | |
4580 | 4575 | | |
4581 | 4576 | | |
4582 | | - | |
| 4577 | + | |
4583 | 4578 | | |
4584 | | - | |
4585 | | - | |
4586 | | - | |
4587 | | - | |
4588 | | - | |
4589 | | - | |
4590 | | - | |
4591 | | - | |
4592 | | - | |
4593 | | - | |
4594 | | - | |
4595 | | - | |
4596 | | - | |
| 4579 | + | |
| 4580 | + | |
| 4581 | + | |
| 4582 | + | |
| 4583 | + | |
| 4584 | + | |
4597 | 4585 | | |
4598 | 4586 | | |
4599 | 4587 | | |
4600 | | - | |
| 4588 | + | |
4601 | 4589 | | |
4602 | | - | |
4603 | | - | |
4604 | | - | |
4605 | | - | |
4606 | | - | |
4607 | 4590 | | |
4608 | | - | |
4609 | | - | |
| 4591 | + | |
| 4592 | + | |
| 4593 | + | |
| 4594 | + | |
| 4595 | + | |
4610 | 4596 | | |
4611 | 4597 | | |
4612 | 4598 | | |
| |||
7000 | 6986 | | |
7001 | 6987 | | |
7002 | 6988 | | |
| 6989 | + | |
7003 | 6990 | | |
7004 | 6991 | | |
7005 | 6992 | | |
| |||
7012 | 6999 | | |
7013 | 7000 | | |
7014 | 7001 | | |
7015 | | - | |
| 7002 | + | |
7016 | 7003 | | |
7017 | | - | |
7018 | | - | |
7019 | | - | |
7020 | | - | |
7021 | | - | |
7022 | | - | |
| 7004 | + | |
| 7005 | + | |
7023 | 7006 | | |
7024 | 7007 | | |
7025 | 7008 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
511 | 511 | | |
512 | 512 | | |
513 | 513 | | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
0 commit comments