I'm getting two type of information with JSON and I'm adding "operations" to 2 different Operation Queues Classes with addObserver(forKeyPath:"operations"...).
In the function observeValue I'm checking if operationQueue1.operations.isEmpty and then I refresh my information in UI. I'm doing the same thing with if else with operationQueue2, but when the 2 operations are started in sometime the application crash with error message: *** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <AppName.ViewController 0x102977800> for the key path "operations" from <AppName.OperationQueue1 0x1c4a233c0> because it is not registered as an observer.'
I don't have problem when only 1 operation is started. Any suggestions?
func getInfo1(){//runned in viewDidLoad
operationQueue1.addObserver(forKeyPath:"operations"...)
operationQueue1.dataTask(URL:"..."....){
DispatchQueue.main.async{
NotificationCenter.default.postNotification(NSNotification.Name(rawValue: "NewDataReceived1", userInfo:infoFromTheWebsite)
}
}
}
func NewDataReceived1(){
here I add the information to arrays to be loaded in tableView1
}
HERE IS THE CODE FOR 2ND INFO WHICH IS THE SAME
override func observeValue(forKeyPath keyPath: String?, ....){
if(object as? operationQueue1 == operationQueue1Class && keyPath == "operations" && context == context1){
if(operationQueue1.operations.isEmpty){
DispatchQueue.main.async{
operationQueue1..removeObserver(self, forKeyPath:"operations")
Timer.scheduled("refreshingTableInformation1")
}
}
}else if(operationQueue2....){
SAME AS OPERATION 1, BUT USING DIFFERENT FUNC TO REFRESH TABLE INFORMATION AND THE TABLES ARE DIFFERENT
}else{
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
func refreshingTableInformation1(){
tableView1.reloadData()
Timer.scheduled("getInfo1", repeat:false)
}
func refreshingTableInformation2(){
tableView2.reloadData()
Timer.scheduled("getInfo2", repeat:false)
}
Sometimes it works 10 secs and crash and sometimes works for more than 60 seconds and then crash...
Your code here is vulnerable to race conditions. Consider the following scenario:
getInfo1() is called, which adds an operation to operationQueue1.
The operation completes, which means your KVO observation is called. The queue is now empty, so your observation schedules removal of your observer on the main dispatch queue.
Now, before the operation you've submitted to the main queue is able to run, something else calls getInfo1(), which adds a new operation to operationQueue1, which completes before the operation you queued in step 2 has had the chance to run (hey, maybe the main queue was busy with something; it's easy for this to happen since it's a serial queue).
Your observation for the first call of getInfo1() is called again while the queue is empty, causing another deregister block to be submitted to the main queue.
The two deregister blocks finally get to execute on the main queue. The second one crashes the program, since you've already deregistered your observer.
You could probably fix this problem (assuming the code doesn't have more problems of this nature) by using Swift 4's block-based observers instead, and setting the observer to nil instead of explicitly deregistering it. However, I propose that KVO is the wrong tool for what you're trying to do. As the instructions for the old "Crystal Quest" game used to say, it's a bit like using an anti-aircraft gun to kill a mosquito.
From what I can see from the code above, it looks like you're using KVO just to schedule a notification for when an operation or a group of operations you submit to the queue finishes. Depending on what your dataTask method actually does, here's what I'd do instead:
If you submit only one operation: set the operation's completionBlock property to a closure that refreshes your table information.
If you submit more than one operation: Make a new BlockOperation which refreshes your table information, and call addDependency on that operation with every other operation you submit to the queue. Then, submit that operation.
This will provide you with a much cleaner and more trouble-free way of monitoring completion for your tasks. And since you don't need the queue to completely empty anymore, you might not even have to use two separate queues anymore, depending on what else you're doing with them.
Related
I receive a lot of data and want to process it off the main thread. Most of the things can be asynchronous in nature, however sometimes there is critical data coming in with each update invalidating the previous iteration. At the moment I set up everything like this:
func onReceivedData(d: Data){ //is on main thread unfortunately
//some work 1
DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async {
//some work 2
if(saveToFile) {
DispatchQueue.global(qos: DispatchQoS.QoSClass.utility).async {
//UserDefaults write
}
}
}
}
Now I am not sure if I understood the DispatchQueue correctly. Is it possible when data A comes in before data B, that data B for some reason may reach the UserDefaults write earlier than data A? Do the DispatchQueues push operations in a serial manner or can they also operate in parallel, potentially writing data B before data A, leaving the UserDefaults in an invalid state?
From the Apple Developer documentation on DispatchQueue.global():
Tasks submitted to the returned queue are scheduled concurrently with respect to one another.
This means that the global() queues are concurrent dispatch queues.
If you want to ensure that the tasks execute in the order that you schedule them, you should create your own serial DispatchQueue.
let queue = DispatchQueue(label: "my-queue")
queue.async {
// Work 1...
}
queue.async {
// Work 2...
}
"Work 1" will always run before "Work 2" in this serial queue (which may not be the case with a concurrent queue).
Alternatively, you could look into using a Swift actor which can more efficiently control access to shared mutable state in a memory-safe way.
In Swift, if Thread.current.isMainThread == false, then is it safe to DispatchQueue.main.sync recursively once?
The reason I ask is that, in my company's app, we had a crash that turned out to be due to some UI method being called from off the main thread, like:
public extension UIViewController {
func presentModally(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
// some code that sets presentation style then:
present(viewControllerToPresent, animated: flag, completion: completion)
}
}
Since this was getting called from many places, some of which would sometimes call it from a background thread, we were getting crashes here and there.
Fixing all the call sites was not feasible due to the app being over a million lines of code, so my solution to this was simply to check if we're on the main thread, and if not, then redirect the call to the main thread, like so:
public extension UIViewController {
func presentModally(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
guard Thread.current.isMainThread else {
DispatchQueue.main.sync {
presentModally(viewControllerToPresent, animated: flag, completion: completion)
}
return
}
// some code that sets presentation style then:
present(viewControllerToPresent, animated: flag, completion: completion)
}
}
The benefits of this approach seem to be:
Preservation of execution order. If the caller is off the main thread, we'll redirect onto the main thread, then execute the same function before we return -- thus preserving the normal execution order that the would have happened had the original function been called from the main thread, since functions called on the main thread (or any other thread) execute synchronously by default.
Ability to implicitly reference self without compiler warnings. In Xcode 11.4, performing this call synchronously also satisfies the compiler that it's OK to implicitly retain self, since the dispatch context will be entered then exited before the original function call returns -- so we don't get any new compiler warnings from this approach. That's nice and clean.
More focused diffs via less indentation. It avoids wrapping the entire function body in a closure (like you'd normally see done if Dispatch.main.async { ... } was used, where the whole body must now be indented a level deeper, incurring whitespace diffs in your PR that can lead to annoying merge conflicts and make it harder for reviewers to distinguish the salient elements in GitHub's PR diff views).
Meanwhile the alternative, DispatchQueue.main.async, would seem to have the following drawbacks:
Potentially changes expected execution order. The function would return before executing the dispatched closure, which in turn means that self could have deallocated before it runs. That means we'd have to explicitly retain self (or weakify it) to avoid a compiler warning. It also means that, in this example, present(...) would not get called before the function would return to the caller. This could cause the modal to pop-up after some other code subsequent to the call site, leading to unintended behavior.
Requirement of either weakifying or explicitly retaining self. This is not really a drawback but it's not as clean, stylistically, as being able to implicitly retain self.
So the question is: are these assumptions all correct, or am I missing something here?
My colleagues who reviewed the PR seemed to feel that using "DispatchQueue.main.sync" is somehow inherently bad and risky, and could lead to a deadlock. While I realize that using this from the main thread would indeed deadlock, here we explicitly avoid that here using a guard statement to make sure we're NOT on the main thread first.
Despite being presented with all the above rationale, and despite being unable to explain to me how a deadlock could actually happen given that the dispatch only happens if the function gets called off the main thread to begin with, my colleagues still have deep reservations about this pattern, feeling that it could lead to a deadlock or block the UI in unexpected ways.
Are those fears founded? Or is this pattern perfectly safe?
This pattern is definitely not “perfectly” safe. One can easily contrive a deadlock:
let group = DispatchGroup()
DispatchQueue.global().async(group: group) {
self.presentModally(controller, animated: true)
}
group.wait()
Checking that isMainThread is false is insufficient, strictly speaking, to know whether it’s safe to dispatch synchronously to the main thread.
But that’s not the real issue. You obviously have some routine somewhere that thinks it’s running on the main thread, when it’s not. Personally, I’d be worried about what else that code did while operating under this misconception (e.g. unsynchronized model updates, etc.).
Your workaround, rather than fixing the root cause of the problem, is just hiding it. As a general rule, I would not suggest coding around bugs introduced elsewhere in the codebase. You really should just figure out where you’re calling this routine from a background thread and resolve that.
In terms of how to find the problem, hopefully the stack trace associated with the crash will tell you. I’d also suggest adding a breakpoint for the main thread checker by clicking on that little arrow next to it in the scheme settings:
Then exercise the app and if it encounters this issue, it will pause execution at the offending line, which can be very useful in tracking down these issues. That often is much easier than reverse-engineering from the stack trace.
I agree with the comments that you have some structural difficulties with your code.
But there are still times in which I need code to run on the main thread and I don't know if I'm already on the main thread or not. This has occurred often enough that I wrote a ExecuteOnMain() function just for this:
dispatch_queue_t MainSequentialQueue( )
{
static dispatch_queue_t mainQueue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
#if HAS_MAIN_RUNLOOP
// If this process has a main thread run loop, queue sequential tasks to run on the main thread
mainQueue = dispatch_get_main_queue();
#else
// If the process doesn't execute in a run loop, create a sequential utility thread to perform these tasks
mainQueue = dispatch_queue_create("main-sequential",DISPATCH_QUEUE_SERIAL);
#endif
});
return mainQueue;
}
BOOL IsMainQueue( )
{
#if HAS_MAIN_RUNLOOP
// Return YES if this code is already executing on the main thread
return [NSThread isMainThread];
#else
// Return YES if this code is already executing on the sequential queue, NO otherwise
return ( MainSequentialQueue() == dispatch_get_current_queue() );
#endif
}
void DispatchOnMain( dispatch_block_t block )
{
// Shorthand for asynchronously dispatching a block to execute on the main thread
dispatch_async(MainSequentialQueue(),block);
}
void ExecuteOnMain( dispatch_block_t block )
{
// Shorthand for synchronously executing a block on the main thread before returning.
// Unlike dispatch_sync(), this won't deadlock if executed on the main thread.
if (IsMainQueue())
// If this is the main thread, execute the block immediately
block();
else
// If this is not the main thread, queue the block to execute on the main queue and wait for it to finish
dispatch_sync(MainSequentialQueue(),block);
}
A bit late, but I had a need for this type of solution too. I had some common code that could be invoked from both the main thread and background threads, and updated the UI. My solution to the generic use case was:
public extension UIViewController {
func runOnUiThread(closure: #escaping () -> ()) {
if Thread.isMainThread {
closure()
} else {
DispatchQueue.main.sync(execute: closure)
}
}
}
Then to call it from a UIViewController:
runOnUiThread {
code here
}
As others have pointed out, this is not completely safe. You might have some code on background thread that is invoked from the main thread, synchronously. If that background code then calls the code above, it will attempt to run on the main thread and will create a deadlock. The main thread is waiting for the background code to execute, and the background code will wait for the main thread to be free.
I've been looking into how to implement a Metronome countdown with the AudioKit lib but ran into an issue related to threads and my implementation.
My implementation uses the AudioKit AKMetronome, a method to handle the metronome.start and the metronome.callback handler to start recording after a given number of bars are completed.
init () {
metronome.callback = handler
}
func record () {
self.metronome.start()
}
The handler computes the metronome position in time and if the desired number of bars completes (countdown), the recorder starts.
Unfortunately, running the .record of AKNodeRecorder in the .callback handler of AKMetronome causes the warning message:
Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
For that reason, the call to start recording in the metronome.callback handler is passed to the main thread through the GCD API:
DispatchQueue.main.sync {
do {
try self.recorder.record()
} catch {
print("[ERROR] Oops! Failed to record...")
}
}
I've used the .sync blocking method to resolve the computation immediately as possible since
timing is critical in audio applications (the call should be executed in a real-time thread, ideally); Is my understanding that the GCP API main thread provides the highest priority, but I'm not certain if that's the best option for a time-sensitive application?
Would like to get some feedback or direction if possible from other users, thank you!
OK, so the actual question has nothing to do with metronomes or countdowns? What you really want to know is: will using sync from a background thread get me onto the main thread faster?
If so: Basically no. This is not what sync is for or what it means. The async/sync difference has absolutely no effect on speed. If you get onto the main thread using async, you will get on as soon it is free, so you gain nothing whatever by saying sync.
Moreover, if you already arrived into the callback in an async background thread, any damage that can be done is already done; your timing is now inexact and there is absolutely nothing you can do about it unless you happen to have a time machine in your pocket.
Following up on #matt advice regarding calls inside callbacks, the approach I took change based in a .scheduledTimer of Timer.
func record () {
metronome.restart()
Timer.scheduledTimer(withTimeInterval: self.barLength, repeats: false) { _ in
self.startRecording()
print("Timer fired!")
}
}
The record is a handler that initiates the AKMetronome, opted to use .restart so that every time's requested starts from the very start.
A property .barLength holds the value for the computed length of the desired countdown. The .startRecording method that is called, is a handler that takes care of AKRecorder .record call.
Future reader, have in mind that Timer is not meant to be accurate according to (Accuracy of NSTimer) but
unfortunately, this is the best approach I've tested and I found so far.
Can I have your advice on the approved approach?
I have four processes that need to run sequentially:
calculateProcess1()
calculateProcess2()
calculateProcess3()
calculateProcess4()
These calculate data and update progress bars (circular) and other screen literals using dispatch queues to make the UI update.
Run them as is and lo and behold they all fire off simultaneously (what a surprise!).
How do I make the top level calls run in sequence (as above) and do I need to make changes to my DispatchQueues in the processes for updating the literals, e.g.:
DispatchQueue.main.sync {
let progressPercentage = (day*100/365)
self.progressLabel.text = String(Int(progressPercentage))+"%"
}
The initiating processes (calculateProcess1-4()) need to run from main. Should this be in ViewDidLoad, a button action or what is the most secure method?
A one approach would be to use Operation and OperationQueue. Setting maxConcurrentOperationCount: Int to 1 on the OperationQueue, will force operations to be performed in a sequence, one after another.
(Previously called NSOperation and NSOperationQueue)
This blogpost can be helpful: https://nshipster.com/nsoperation/
And of course awesome WWDC15 session: Advanced NSOperations
1- Don't sync in main thread as it'll cause a runtime crash , it should be async
DispatchQueue.main.sync {
2-
Should this be in ViewDidLoad, a button action or what is the most secure method?
it's up to you there is no thing related to security here , implement it as your UX
3- to run them in dequence create a custom Queue then dispatch them in it as it' ll run serially if the code inside all these methods run in the same thread of the queue meaning you don't internally dispatch inside another queue , you can also use DispatchGroup to be notified when all are done
If you want to keep it simple you can provide callback blocks to your calculateProcess methods and simply nest them.
calculateProcess1() {
calculateProcess2() {
calculateProcess3() {
calculateProcess4() {}
}
}
}
Should this be in ViewDidLoad, a button action or what is the most
secure method?
viewDidLoad is probably what you want. Keep in mind if you instantiate a new viewController of the same type and show it, it will happen again, once, for that new controller as well. Also, if you are doing anything with view frames, those are not guaranteed to be laid out in viewDidLoad, but if you are simply updating text then it should be fine.
In the code that I show below, I have created a thread that creates random numbers between 0 to 15, and it stops when it comes out 3, changing the end parameter. After I added a run loop observer (that "observe" the end parameter) to the run loop of the main thread. As you can see, both the run loop observer and my thread, sleep for 1 second before printing, so I expect that in the console, observer's print and the print of my thread is alternate. Which is not the case. I believe, if I understand it, it would depend on CFrunloopActivity parameter and its possible combinations.
Does anyone can explain the operation of this parameter?
If yes, there is a combination to have alternating prints? If you can not have alternating prints, how does the observer work inside the run loop of the main thread ?
Thank you
This is the code :
class ViewController: UIViewController {
var end = false
override func viewDidLoad() {
super.viewDidLoad()
//my thread
performSelector(inBackground: #selector(rununtil3(thread:)), with: Thread.current)
//the observer
let runLoopObserver = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, CFRunLoopActivity.entry.rawValue | CFRunLoopActivity.exit.rawValue , true , 0 , {
(observer: CFRunLoopObserver?, activity: CFRunLoopActivity) -> Void in
Thread.sleep(until: Date(timeIntervalSinceNow: 1))
print("+++ is main?: \(Thread.isMainThread)")
if self.end == true {
//print the end of my thread and remove the observer from main run loop
print("end of own thread")
CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, CFRunLoopMode.commonModes)
return
}
//CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, CFRunLoopMode.commonModes)
})
//add observer to main run loop
CFRunLoopAddObserver(CFRunLoopGetCurrent(), runLoopObserver, CFRunLoopMode.commonModes)
print("Out of observer")
}
func rununtil3(thread : Thread) {
print("main?: \(thread.isMainThread) is run : \(thread.isExecuting)")
while true {
let ran = Int (arc4random() % 15 )
Thread.sleep(until: Date(timeIntervalSinceNow: 1))
print("\(ran) . main is run : \(thread.isExecuting)")
if ran == 3 {
end = true
Thread.exit()
}
}
}
}
You are creating a runloop observer and asking to be notified when the runloop either starts operation (its enter event) or exits. Runloop activities are documented here.
enter events are delivered when a runloop is started by CFRunLoopRun() or similar. If you manually create a runloop, add an enter observer to it, then call CFRunLoopRun() on your new runloop, you will receive an enter event at that time. If you later call CFRunLoopStop() on your runloop, you will receive an exit event.
When you add an enter observer to an already running runloop, you will receive an enter event. This is to keep your observer state consistent with the actual state of the runloop.
In your code, you create a runloop observer then attach it to the main thread's runloop (aka the "Main Runloop").
The OS creates the Main Runloop automatically for you at program start and automatically calls CFRunLoopRun() on it. CFRunLoopStop() is never called, so the main runloop effectively runs forever.
Since the main runloop is already running, you receive an enter event. Since the main runloop does not stop, you never see an exit event.
It's important to note that a runloop observer is bound to the specific runloop you add it to, not to the lifecycle of some arbitrary background thread or property (i.e. your end property is not the thing being observed).
To your second question about how to get threads to alternate, that's a question with a very broad answer and it depends highly on what you want to do. I won't attempt to answer all of that here, only give you some ideas.
You might be best served by not creating a background thread at all, and instead adding a timer to the main runloop that fires every second. Then you'll get periodic behavior.
If you really want to use a background thread, then you should read a good operating systems book on thread communication and synchronization. A common thing to do on iOS/OS X is to use a background thread, then use something like DispatchQueue.main.async { } to signal back to the main thread that your processing is complete. There are many examples of how to do this if you search a bit.
You might also read about thread synchronization using semaphores or condition variables. One thing you definitely do NOT want to do is call Thread.sleep() on the main thread as you are doing in your observer callback. If you wait on the main thread too long the operating system will kill your app. It's better to keep background threads totally independent of the main thread and then call back onto the main thread using a DispatchQueue call as I mentioned above in #2.