CoreFx #16274 Implementation - New overloads of LazyInitializer.EnsureInitialized for reference types#9831
Conversation
| /// <param name="syncLock">A reference to an object used as the mutually exclusive lock for initializing | ||
| /// <paramref name="target"/>. If <paramref name="syncLock"/> is null, a new object will be instantiated.</param> | ||
| /// <returns>The initialized value of type <typeparamref name="T"/>.</returns> | ||
| public static T EnsureInitialized<T>(ref T target, ref object syncLock) where T : class |
There was a problem hiding this comment.
I don't believe this API was discussed or approved, was it? Wasn't it just the one that takes a delegate?
There was a problem hiding this comment.
@WinCPP any API change has to be first concluded and approved - it is best done in issues. PRs are bad place to do API review discussions. Please revert it for now and restart the discussion in the issue (or create a new one).
There was a problem hiding this comment.
@karelz our comments crossed... yes I understand it is not a good idea. I am removing the API. Build and test is in progress and will push the same.
| if (Volatile.Read(ref target) == null) | ||
| { | ||
| target = valueFactory(); | ||
| if (target == null) |
There was a problem hiding this comment.
This should be: Volatile.Write(ref target, valueFactory());
There was a problem hiding this comment.
Changing this. It will force the write of target.
|
@stephentoub , the changes are in. Sorry. |
| } | ||
|
|
||
| return EnsureInitializedCore<T>(ref target, ref syncLock, valueFactory); | ||
| } |
There was a problem hiding this comment.
Nit: I realize you're just copying the style used above; that's fine. We could subsequently shorten / clean this up as:
public static T EnsureInitialized<T>(ref T target, ref object syncLock, Func<T> valueFactory) where T : class =>
Volatile.Read(ref target) ??
EnsureInitializedCore<T>(ref target, ref syncLock, valueFactory);f82f946 to
a8d9a1a
Compare
| slock = newLock; | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Since this code is now duplicated in two different methods, could you separate it out into a helper? e.g.
private static object EnsureLockInitialized(ref object syncLock) =>
syncLock ??
Interlocked.CompareExchange(ref syncLock, new object(), null) ??
syncLock;which you could then use here and in the other function as:
lock (EnsureLockInitialized(ref syncLock))
{
...
}?
There was a problem hiding this comment.
Yup. Makes sense. Actually out of habit, I was into the groove of 'don't touch something which already works; follow the style'...
private static object EnsureLockInitialized(ref object syncLock) =>
syncLock ??
Interlocked.CompareExchange(ref syncLock, new object(), null) ??
syncLock;Quick Qestion: The '?? syncLock' towards end of statement made me scratch my head a bit... typo, not intentional... right...? Really bad question... treated it like Interlocked.Increment which returns the incremented value...
a8d9a1a to
283f386
Compare
|
@stephentoub the changes are ready... |
|
|
||
| // Lazily initialize the lock if necessary and, then double check if initialization is still required. | ||
| lock (EnsureLockInitialized(ref slock)) | ||
| { |
There was a problem hiding this comment.
This is not correct. It needs to be:
lock (EnsureLockInitialized(ref syncLock))By initializing the local, racing threads will all end up creating and using their own slock object. You can delete the slock local.
|
|
||
| // Lazily initialize the lock if necessary and, then double check if initialization is still required. | ||
| lock (EnsureLockInitialized(ref slock)) | ||
| { |
There was a problem hiding this comment.
Same problem. This needs to be ref syncLock, not ref slock. You can delete the local slock.
There was a problem hiding this comment.
Yes, agreed. This is because of oversight. My test cases ran successfully because I added one for non-null syncLock to the new LazyInitializer.EnsureInitialized API and was happy with its success. I will add another test case for scenario that the user sends in a null syncLock which would have set off the race condition.
| /// <param name="syncLock">A reference to a location containing a mutual exclusive lock. If <paramref name="syncLock"/> is null, | ||
| /// a new object will be instantiated.</param> | ||
| /// <returns>Initialized lock object.</returns> | ||
| private static object EnsureLockInitialized(ref object syncLock) => syncLock ?? Interlocked.CompareExchange(ref syncLock, new object(), null) ?? syncLock; |
There was a problem hiding this comment.
Nit: please split this across multiple lines to make it easier to read.
types LazyInitializer.EnsureInitialized overload for reference types that does not take boolean for tracking initialization status of the reference type object. Null check on the object parameter is sufficient to whether or not it has been initialized.
283f386 to
f6533e1
Compare
|
@jkotas, does the mscorlib.cs ref still need to be updated to include new APIs, or is that now defunct? |
|
mscorlib.cs ref is defunct. We can delete it once the CoreCLR tests are fixed to consume live corefx - they are still stuck on corefx before the corefx build changes. |
|
Thanks. |
…Overloads CoreFx dotnet/coreclr#16274 Implementation - New overloads of LazyInitializer.EnsureInitialized for reference types Commit migrated from dotnet/coreclr@38b0df9
This PR is for the implementation of new overloads of LazyInitializer.EnsureInitialized for reference types. Please refer to https://github.com/dotnet/corefx/issues/16274 for more information.
@stephentoub @terrajobst @karelz Kindly review the changes.