-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add setter to TransactionManager.DefaultTimeout and MaxTimeout. #71703
Conversation
Note regarding the This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change. |
src/libraries/System.Transactions.Local/src/System/Transactions/TransactionManager.cs
Show resolved
Hide resolved
} | ||
} | ||
|
||
s_defaultTimeoutValidated = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should DefaultTimeout always be less than MaxTimeout? If so, when setting either of the two property values, the other property value needs to be validated and adjusted accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My intuition says yes, default should always be less than max. What did the config system do in 4.8?
It appears that ValidateTimeout() already handles this case here. But a similar validtation would need to be added in the setter for MaxTimeout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In 4.8 it did one-time loading from config settings and adjust, test result shows default always less than max. I have added ValidateTimeout for default timeout value in the MaxTimeout setter.
} | ||
|
||
s_cachedMaxTimeout = false; | ||
LazyInitializer.EnsureInitialized(ref s_maximumTimeout, ref s_cachedMaxTimeout, ref s_classSyncObject, () => value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there value to using the LazyInitializer pattern here? We already have the value. It's just a simple assignment at this point. Why don't we set s_cachedMaxTimeout to true and just assign the value here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking the same thing. The LazyInitializer handles lazily assigning an object to s_classSyncObject for locking so that might be null. I don't see any reason to need a lock here though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation! It's updated to set s_cachedMaxTimeout to true and assign the value directly.
src/libraries/System.Transactions.Local/src/System/Transactions/TransactionManager.cs
Show resolved
Hide resolved
Thanks for your interest in contributing here. The API in #59282 hasn't been approved yet. We generally discourage PRs for new APIs that haven't yet been approved, but regardless we shouldn't merge this until it is. |
s_cachedMaxTimeout = true; | ||
s_maximumTimeout = value; | ||
s_defaultTimeout = ValidateTimeout(s_defaultTimeout); | ||
s_defaultTimeoutValidated = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should log the same event if the default timeout gets reduced. Save a local copy of s_defaultTimeout before the call to ValidateTimeout, then do a comparison and emit an event similar to the DefaultTimeout setter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Matt! Updated.
TimeSpan tsMax = TimeSpan.Parse("00:30:00"); | ||
|
||
TransactionManager.DefaultTimeout = tsDefault; | ||
Assert.Equal(tsDefault, TransactionManager.DefaultTimeout); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the pipeline run, test sometimes fails (expected 00:02:00, actual 00:01:00) at this line on mono platform. I think the error reproduces more than 80% of the time, I initially thought running the cases in parallel caused the problem, but after writing all the settings in one method, the problem persists. I have no idea how this happened, @StephenMolloy, @mconnew, any thoughts?
@imcarolwang, I just reread the original API proposal and there was a comment by Levi pointing out the potential for data tearing problems in the implementation and to be aware of it. Basically a long is 64bits and on a 32bit platform the two halves are set in two different machine instructions. If you have two different threads setting 2 different values at the same time and you are running on a 32bit platform, you can get the low 32bits from Thread 1 and the High 32bits from Thread 2 and end up with a value which is different from what either thread is trying to set. |
Thank you @mconnew for the comments! This morning I tried the Interlocked.Exchange approach in a different test PR, the same failure happened on "mono OSX x64 Debug" (for current PR it failed initially and passed in a latter re-ran). As Interlocked.Exchange doesn't have overload with TimeSpan so I convert it to ticks with type long, does it look right to you? I was thinking that if the data tearing thing doesn't happen on x64 platform, then the failure is caused by something else. |
|
||
long defaultTimeoutTicks = Interlocked.Read(ref s_defaultTimeoutTicks); | ||
Interlocked.Exchange(ref s_defaultTimeoutTicks, ValidateTimeout(new TimeSpan(Interlocked.Read(ref s_defaultTimeoutTicks))).Ticks); | ||
if (Interlocked.Read(ref s_defaultTimeoutTicks) != defaultTimeoutTicks) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are using Interlocked.Read to read s_defaultTimeoutTicks, but you just did this on the previous line. You can reuse the local defaultTimeoutTicks here instead of doing a second Interlocked.Read
@imcarolwang, once you remove the additional Interlocked, this is good to check in. |
@HongGit , @mconnew , @stephentoub, this is for #71025 and #59282.
Test scenario: