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
[SR-8572] OperationQueue.isSuspended = true suspends the underlying DispatchQueue #3644
Comments
Yes, this would be a huge problem for Alamofire 5, as the root queue is now shared amongst all requests by default, which only works because of the Darwin behavior. We could refactor to replicate the behavior using raw DispatchQueues, but I'd rather keep the simple solution of an OperationQueue on top of the DispatchQueue. As an aside, can anyone tell me if a DispatchQueue initialized with a targetQueue will suspend the target if it's suspended? That could be another solution if it doesn't. |
Comment by Maksim Orlovich (JIRA) Hi @jshier. Thanks for your input wrt this bug. I'm not sure of the behavior of DispatchQueue's on Linux when it comes to the target DispatchQueue's. I'll try and experiment and post my findings here. I suspect it will match the Darwin behavior as https://github.com/apple/swift-corelibs-libdispatch appears to be based on Apple's libdispatch (https://opensource.apple.com/source/libdispatch/)... By the way, here's the initial support for Alamofire 5 on Linux: I had to stub out unsupported features like ServerTrust evaluators and URLTaskSessionMetrics, but for the most part, things seem to work. I've got about 200 unit tests passing out of the box, however, this bug and https://bugs.swift.org/browse/SR-8542 are causing problems... |
Comment by Maksim Orlovich (JIRA) @jshier I have tried the experiment with DispatchQueue's targets and behavior on Ubuntu is the same as on Darwin. Here's what I tried: import Foundation let dq1 = DispatchQueue(label: "org.swift.queue1") //dq1.suspend() var done1 = false dq1.async { dq2.async { dq1.resume() repeat { print("DONE") Calling dq1.suspend() appears to suspend both queues (neither statement is printed). However, calling dq2.suspend() appears to suspend only DQ2 and dq1 continues to run. This behavior is the same on both Ubuntu and on Darwin. |
Comment by Maksim Orlovich (JIRA) Fixed in Swift 4.2 release and in master branch. |
Environment
Ubuntu 16.04 / Swift 4.2 (but also earlier versions)
Additional Detail from JIRA
md5: cfbe7022ce2b9cf366f599f22d732b9b
Issue Description:
The current implementation of OperationQueue suspends the underlying DispatchQueue when the user calls OperationQueue.isSuspended = true (unlike on Darwin, which only suspends the given OperationQueue, but not the underlying DispatchQueue).
Consider the following code:
import Foundation
let rootQueue = DispatchQueue(label: "org.swift.queue")
let delegateQueue = OperationQueue()
delegateQueue.qualityOfService = .default
delegateQueue.maxConcurrentOperationCount = 1
delegateQueue.underlyingQueue = rootQueue
delegateQueue.isSuspended = true
// Should run
var doneAsync = false
rootQueue.async {
print("on request queue")
doneAsync = true
delegateQueue.isSuspended = false
}
// Should not run until block above runs and resumes this OperationQueue
var doneOperation = false
delegateQueue.addOperation {
print("on internal queue")
doneOperation = true
}
repeat {
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
} while doneAsync == false || doneOperation == false
print("DONE")
On Darwin, this completes without any issues. On Ubuntu, Swift 4.x (and probably 3.x), this hangs.
I discovered this issue while porting Alamofire to run on Linux. Alamofire is relying on this type of a pattern, but because of this bug, requests never complete.
The problem lies around line 506 here: https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/Operation.swift
queue.suspend()/queue.resume()
I am not very familiar with the code in Operation.swift, but I think the fix is straightforward.
1] Stop calling suspend/resume
2] In addOperations() call, prepare DispatchWorkItem() as it is currently, but if the state is suspended, put these objects in an array of pending items instead of scheduling on the _underlyingQueue
3] When the state is changed from suspended to resumed -> immediately put pending DispatchWorkItems() on the underlying queue.
I would really like to see this fixed in Swift 4.2 release.
@phauslerdgrove-oss (JIRA User)@parkera you guys were the original authors of this code. What do you think of the proposed fix?
Also CC'ing @jshier as the author of Alamofire 🙂
The text was updated successfully, but these errors were encountered: