-
Notifications
You must be signed in to change notification settings - Fork 1.6k
iOS: fix data race in FSTDispatchQueue. #1070
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
Conversation
operationInProgress is accessed from both the main thread and from libdispatch on some other thread. Make it atomic to avoid a data race. Tested: ran unit tests using old and new versions under Thread Sanitizer, verified that TSan reports a data race for the old version, but finds no issues with the new version.
@@ -16,6 +16,8 @@ | |||
|
|||
#import <Foundation/Foundation.h> | |||
|
|||
#import <atomic> |
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 believe I just added checks to cpplint to help with that and it has called you out: https://travis-ci.org/firebase/firebase-ios-sdk/jobs/365187148#L641 :-).
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.
Yes, I deleted the comment once I noticed, but you were quicker.
@@ -145,20 +147,20 @@ - (void)markDone { | |||
|
|||
#pragma mark - FSTDispatchQueue | |||
|
|||
@interface FSTDispatchQueue () | |||
@interface FSTDispatchQueue () { |
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 can add an instance variable here, right?
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.
Yes, though customarily it's just done on the @implementation
.
We use the empty category to make "private" declarations that must be in an @interface
. Things like property declarations or a hidden designated initializer.
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.
Hmm, it appears that if I move the variable declaration inside @implementation
, it becomes a global variable, not instance variable.
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.
The declaration must still be inside braces. See e.g.
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.
Ah, thanks for the explanation. Done.
* Flag set while an FSTDispatchQueue operation is currently executing. Used for assertion | ||
* sanity-checks. | ||
*/ | ||
@property(nonatomic, assign) BOOL operationInProgress; |
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.
If I change the property to be atomic
, TSan still reports a data race. I probably don't understand how Objective-C atomic
works.
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.
Objective-C properties are a code-generation mechanism. The attributes here manipulate the generated code for the getter and setter. In this case, the value is being accessed through the instance variable directly so the change has no effect.
If you changed the references from _operationInProgress
to self.operationInProgress
(and flipped the atomic attribute) that should also fix this.
@wilhuff Gil, a couple of notes:
|
@@ -16,6 +16,8 @@ | |||
|
|||
#import <Foundation/Foundation.h> | |||
|
|||
#import <atomic> |
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 believe I just added checks to cpplint to help with that and it has called you out: https://travis-ci.org/firebase/firebase-ios-sdk/jobs/365187148#L641 :-).
@@ -145,20 +147,20 @@ - (void)markDone { | |||
|
|||
#pragma mark - FSTDispatchQueue | |||
|
|||
@interface FSTDispatchQueue () | |||
@interface FSTDispatchQueue () { |
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.
Yes, though customarily it's just done on the @implementation
.
We use the empty category to make "private" declarations that must be in an @interface
. Things like property declarations or a hidden designated initializer.
* Flag set while an FSTDispatchQueue operation is currently executing. Used for assertion | ||
* sanity-checks. | ||
*/ | ||
@property(nonatomic, assign) BOOL operationInProgress; |
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.
Objective-C properties are a code-generation mechanism. The attributes here manipulate the generated code for the getter and setter. In this case, the value is being accessed through the instance variable directly so the change has no effect.
If you changed the references from _operationInProgress
to self.operationInProgress
(and flipped the atomic attribute) that should also fix this.
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.
LGTM
An alternative would be to change the order of this assertion:
That is to say that we allow dispatch async from outside the queue or from within the queue if there's no operation already in progress. |
This seems reasonable. TSan is happy with the change as well. The only thing I'm not sure about is what prevents a race with What's your preference? I can push the commit that does this instead of an atomic. |
I'm not sure there is anything preventing that race, so likely we should do both (reorder the check and leave this as an atomic). |
Done. Since you approved the |
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.
LGTM, thanks!
Thanks for the review. I'll merge once the checks complete. |
operationInProgress is accessed from both the main thread and from libdispatch on some other thread. Make it atomic to avoid a data race. Also reorder assertion checks to only access operationInProgress after making sure the function is running on the queue. Tested: ran unit tests using old and new versions under Thread Sanitizer, verified that TSan reports a data race for the old version, but finds no issues with the new version.
operationInProgress is accessed from both the main thread and from
libdispatch on some other thread. Make it atomic to avoid a data race.
Tested: ran unit tests using old and new versions under Thread
Sanitizer, verified that TSan reports a data race for the old version,
but finds no issues with the new version.