Skip to content
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

Implement sync_ barrier based concurrency management #303

Closed
wants to merge 10 commits into from
Closed

Implement sync_ barrier based concurrency management #303

wants to merge 10 commits into from

Conversation

flovilmart
Copy link
Contributor

@flovilmart flovilmart commented Aug 25, 2017

Addresses issue #302
Also implements BoltsSwift resource protection through a queue and barrier_sync

cc @nlutsenko as you were marked in the TODO.

@nlutsenko
Copy link
Member

@flovilmart, this is a good approach, though I would recommend looking at how we dealt with task state in Bolts-Swift and potentially porting it over here.

I will give it a tad more time to review, since it’s crucial to make sure that we’ll never get a deadlock, where we might have cases not covered by tests right now.

Also cc @richardjrossiii

@flovilmart
Copy link
Contributor Author

Yeah, I encountered a few deadlocks while developing it. I’ll check out the Swift implementation for the state management, and improve.

@flovilmart
Copy link
Contributor Author

@nlutsenko I introduced the BFTaskState, not sure if that's what you had in mind.

@flovilmart
Copy link
Contributor Author

@nlutsenko We'll release our future QA version with this custom build, it all look good so far, no more thread sanitizer alert, no deadlocks.

return _result;
}
__block id result;
dispatch_barrier_sync(_synchronizationQueue, ^{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You only need to use barrier_sync for writes - for reads, just use dispatch_sync. You'll get read-write lock behavior here, with a slight perf improvement.

@@ -16,6 +16,13 @@

NS_ASSUME_NONNULL_BEGIN

typedef enum : NSUInteger {
pending,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use more descriptive names here. BFTaskStatePending, BFTaskStateSucceeded, etc.

[self.callbacks removeAllObjects];
});
for (void (^callback)() in callbacks) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a reason IIRC that we had the callbacks executing in the context of self.lock before. I believe it had to do with a race whereupon you could enqueue a new callback in-between -removeAllObjects and execution of the callbacks, and allow another continuation to execute out-of-order.

If I recall from the conversations we had at the time, we weren't comfortable changing those guarantees for people who were using Bolts already, whereas Bolts-Swift was able to break those guarantees as they didn't exist yet.

I'm OK with switching it to this version, I just wanted to make sure the context is documented here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I recall I had issues with keeping the execution in the sync block, perhaps outside a barrier is better, LMK.

Copy link
Contributor Author

@flovilmart flovilmart Aug 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BOOL completed = self.isCompleted;
dispatch_barrier_sync(_synchronizationQueue, ^{
if (!completed) {
[self.condition lock];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is technically undefined behavior here. There's no guarantee on the thread that synchronizationQueue runs on, and NSConditionLock requires you to unlock from an identical thread. We may need to rethink this. (What's the bolts-swift way of doing this?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems very similar: https://github.com/BoltsFramework/Bolts-Swift/blob/master/Sources/BoltsSwift/Task.swift#L211

main difference is that the NSConditionLock is created set on the queue, let me know if you prefer that way.

if (self.completed) {
return;
BOOL completed = self.isCompleted;
dispatch_barrier_sync(_synchronizationQueue, ^{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're locking twice here by going through the property (use ivar instead?). Theres also a TOCTOU race condition here.

__block NSString *resultDescription = nil;

dispatch_barrier_sync(_synchronizationQueue, ^{
isCompleted = _state != pending;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just take the state, result pair out, don't do any additional work inside the lock. Acquire the lock for as little time as is safely possible.

@flovilmart
Copy link
Contributor Author

@richardjrossiii I believe I addressed your suggestions let me know if there's anything else. I know this is a tricky part with concurrency and I'm a bit rusted on those problematics.

@flovilmart
Copy link
Contributor Author

flovilmart commented Sep 8, 2017

@richardjrossiii @nlutsenko the Parse SDK is all fine with that version and the thread sanitizer is happy for what I can tell. See: https://travis-ci.org/parse-community/Parse-SDK-iOS-OSX/builds/273015827

parse-community/Parse-SDK-iOS-OSX#1185

@flovilmart
Copy link
Contributor Author

@richardjrossiii @nlutsenko a quick ping on that one, I'm open to re-visiting another option that would let Xcode not warn about potential unsafe memory accesses.

@flovilmart
Copy link
Contributor Author

@nlutsenko ping?

@flovilmart
Copy link
Contributor Author

@nlutsenko any time for the review?

@tealshift
Copy link

tealshift commented Apr 28, 2018

I'm a bit out of my depth here. Can anyone can confirm this issue has potential to cause instability? I.e. would solving the problem actually make Bolts more stable, or is this mainly to silence Xcode's thread sanitizer tool? @nlutsenko @flovilmart

@flovilmart flovilmart closed this May 22, 2019
@flovilmart flovilmart deleted the thread-sanitation branch May 22, 2019 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants