-
Notifications
You must be signed in to change notification settings - Fork 26
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
[WIP] Make the library thread-safe. #620
Conversation
A side effect I didn't see coming from this is that, since more operations are done asynchronously and the library holds to weak references even less time, #607 is even worse now. Something like this doesn't work:
Now users will really need to make sure their |
The test suite is passing now. ThreadSanitizer is still coughing out issues, though. |
🎆
Oh, well at least we're getting the warnings I suppose. |
**Work in progress!** There are still some deadlocks around. The test suite is pretty broken right now. This change makes the library thread-safe. Users can call all methods and access all properties from whatever thread they want. To achieve this, this is the general strategy used: * We have a serial queue where all our code runs. * To ensure all our code runs in the serial queue, we need to dispatch to it from all entry points, namely: - User code: all public methods use - HTTP requests: all HTTP responses are handled at the same place and dispatched there. - Reachability callback from the OS. - Events dispatches/timeouts from ARTEventEmitter, which use NSNotificationCenter under the hood: all are dispatched to the queue. - WebSocket incoming messages: we set the queue as delegate using SocketRocket's `setDelegateQueue`. * By default, the queue's delegate is the global queue with QOS_CLASS_BACKGROUND. So all our operations will be very low-priority, which I think is the right choice for Ably. The user can change this by specifying `ARTClientOptions.internalDispatchQueue`. * Additionally, callbacks provided from the user are dispatched to another queue, which by default is the main queue. This is a convenience, since they probably want to react to Ably's callbacks by updating the UI, which only can be done from the main queue. This can be overriden too, with `ARTClientOptions.dispatchQueue`. * Public, mutable properties (e. g. connection and channel state) need always to be read and written to from the queue. This is pretty annoying since those reads need to be synchronous, since we're offering a synchronous interface. A synchronous dispatch to a queue is annoying since you can't do it if you're in the queue already, so, from within the queue (ie. from our code) we need to read those properties without the sync dispatch. So those get an additional `<property name>_nosync` getter. Pretty clumsy but, well, this is Objective-C. * Some public methods don't go async right away, since they have some effects that need to be done right after the call, or need to return something. For instance, `attach` leaves the channel in the ATTACHING state synchronously. Similarly to public, mutable properties as described above, those method get an additional method without the sync dispatch, to use from inside our code.
Don't rely on weak references to the ARTRest.
Exceptions don't play nice with GCD dispatches.
Avoid crashes on test failures.
Use the new waitFor helper instead. Also, use proper type params.
It was just too easy to accidentally cause a deadlock with the main thread, since waitUntil is probably using it. It's a hack. Better to avoid it. Also, improve error reporting, and simplify by using waitFor.
The main thread may be in use, and best to avoid Ably threads.
I believe this is finally ready. |
Woop woop. Thanks @tcard |
Work in progress! There are still some deadlocks around. The test suite is pretty broken right now.
This change makes the library thread-safe. Users can call all methods and access
all properties from whatever thread they want.
To achieve this, this is the general strategy used:
from all entry points, namely:
setDelegateQueue
.ARTClientOptions.internalDispatchQueue
.ARTClientOptions.dispatchQueue
.<property name>_nosync
getter. Pretty clumsy but, well, this is Objective-C.attach
leaves the channel in the ATTACHING state synchronously. Similarly to public, mutable properties as described above, those method get an additional method without the sync dispatch, to use from inside our code.