IL: make ILTypeDefs, ILMethodDefs thread-safe #16147
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
InlineDelayInit
is used inILTypeDefs
andILMethodDefs
. It's not thread safe and creates unneeded closured for all types and methods to be possibly imported.The thread safety problem can be seen here:
There's a chance that
x.func
may be set tonull
on another thread beforeEnsureInitialized
is executed, leading to exception and possibly corrupted compiler state.It wasn't a problem before, as all this compiler logic was single-threaded. Nowadays we try to move parts of the analysis to separate threads (think about 1) skipping implementation files and then analyzing them concurrently, 2) sharing
ILModuleDef
between multiple background builders in an IDE, 3) moving some parts of the analysis like pattern match compilation to a later parallel stage). All of this makes it possible for this problem to occur if two threads try to import the same namespace or methods at the same time.In addition to not being thread-safe,
InlineDelayInit
always allocated another closure for the dictionary factory as it used array value. It also allocatedFunc
and kept delegates to store the factories.The current implementation uses a monitor to fix thread safety, doesn't allocate
Func
delegates, and stores a single array factory lambda instead of two.Since there're many instances created and we want to use less memory here, we can also shave another empty object and a reference field from the type if we decide that it's fine to lock on
this
, but that's a questionable trick. Or we could pass/use some shared object instead.It's also possible to remove some added repetition, but I've only managed to do it at the expense of additional allocations when creating the array or dictionary, so I didn't commit these changes.