Recently I encountered a WebKit crash problem, it is related to thread checking
0 WebKit WTFCrashWithInfo(int, char const*, char const*, int)()
1 WebKit WebKit::isMainThreadOrCheckDisabled()()
2 WebKit WebKit::WebProcessPool::createNewWebProcess(WebKit::WebsiteDataStore*, WebKit::WebProcessProxy::IsPrewarmed)()
3 WebKit WebKit::WebProcessPool::processForRegistrableDomain(WebKit::WebsiteDataStore&, WebKit::WebPageProxy*, WebCore::RegistrableDomain const&)()
4 WebKit WebKit::WebProcessPool::createWebPage(WebKit::PageClient&, WTF::Ref<API::PageConfiguration, WTF::DumbPtrTraits<API::PageConfiguration> >&&)()
5 WebKit -[WKContentView _commonInitializationWithProcessPool:configuration:]()
6 WebKit -[WKContentView initWithFrame:processPool:configuration:webView:]()
7 WebKit -[WKWebView _initializeWithConfiguration:]()
8 WebKit -[WKWebView initWithFrame:configuration:]()
I am sure the WKWebView was initialized on Main Thread, but isMainThreadOrCheckDisabled shows isn't. I even read the source code of WebKit, not able to find isMainThreadOrCheckDisabled be called in createNewWebProcess ,so do you have any idea?
After further reading the source code of WebKit, I find out
isMainThreadOrCheckDisabled check thread by RunLoop actually, which is
bool RunLoop::isMain()
{
ASSERT(s_mainRunLoop);
return s_mainRunLoop == &RunLoop::current();
}
and the &RunLoop::current() represents current thread, so s_mainRunLoop means Main Thread, because it is initialized on it
void RunLoop::initializeMain()
{
RELEASE_ASSERT(!s_mainRunLoop);
s_mainRunLoop = &RunLoop::current();
}
So, when run isMainThreadOrCheckDisabled on a background thread, it return false.
But the crash I encountered was happened on Main Thread, dose it means s_mainRunLoop not initialized on Main Thread? Or, is there any situation can WebKit be initialized on background thread?
Related
Our app is crashing with the fatal exception:
Fatal Exception: NSInternalInconsistencyException
Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.
Our app used to not throw this exception on previous versions of iOS (12 and below) and when I would compile on Xcode 10. Now, on iOS 13.3.1 and compiling on Xcode 11.3.1 our app crashes 10 seconds after opening every time. I looked into this exception and it seems Apple has decided to prevent apps from accessing the UIKit on background threads (please correct me if I'm wrong here). After enabling the Main Thread Checker I was able to pinpoint the line that was causing this crash. Backtracing the code led me to this line:
dispatch_async(dispatch_get_main_queue(), ^{
//Code that uses the StoreKit to retrieve purchase data,
//then updates the UI using UIKit based on this information
});
This code is fine though right? Because even though it's dispatching this block of code, it's doing it to the main thread, so this shouldn't be the issue, correct? Well, even after moving this block of code out of the dispatch_async() function, it still crashes. So then I looked at the singleton for this class, which looks like this:
+(instancetype)sharedPurchaseManager
{
static PurchaseManager * sharedPurchaseManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedPurchaseManager = [[PurchaseManager alloc] _init];
});
return sharedPurchaseManager;
}
And I wondered if the _init() for the PurchaseManager was happening in a background thread. So I tried moving the allocation for PurchaseManager out of the dispatch_once() block and the app stopped crashing. Furthermore, the Main Thread Checker stopped interrupting the program at the line that uses UIKit. I'm pretty sure that moving my _init() code out of the dispatch_once() block is not the correct way to go about fixing my issue, as this is the standard singleton design pattern (please correct me if I'm wrong). So then how should I go about fixing this issue? Furthermore, we have about 10+ crashes in our code that is due to the same exception, and with a similar design pattern to our PurchaseManager, so we would want a solution for all of these exceptions.
It sounds like [[PurchaseManager alloc] _init] contains some UI code, but +(instancetype)sharedPurchaseManager is accessed first time from background thread.
So you can try two approaches:
1) find the way explicitly initialize sharedPurchaseManger on the main thread (eg. in applicationDidFinishLaunching)
2) try to use the following
dispatch_once(&onceToken, ^{
dispatch_sync(dispatch_get_main_queue(), ^{ // << intentionally synchronous!
sharedPurchaseManager = [[PurchaseManager alloc] _init];
}
});
I have a fresh iOS app / project; all I've done is import the HomeKit framework. When I instantiate HMHomeManager:
var homeManager = HMHomeManager()
Xcode yells at me:
Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState]
... this occurs even if I wrap it in DispatchQueue.main.async {}
Since this project is effectively blank, I'm assuming this an Xcode bug ... but am I doing something wrong?
This warning leads to a serious problem cause I really can't call the delegate outside of the main thread using Xcode 9 beta 2. Strange thing is that this was working when I was using Xcode 8.3.3.
Also I thought it would only be good practice to call delegates from main thread only, isn't it? So why is this causing the app to crash now?
Just call it from the main thread like this.
Objective-C
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication delegate] fooBar];
});
Swift
DispatchQueue.main.async {
YourUIControlMethod()
}
Reaching out to your app delegate like this, is a hint that your architecture could use a little cleanup.
You can call delegates from any thread you want. You only need to make sure you're on the main thread for UIKit calls.
Or that you're on the correct thread your CoreData objects expect. It all depends on the API contract your objects have.
In Swift, you could also use DispatchQueue.main.async to call the UI controlling method from the main thread
DispatchQueue.main.async {
YourUIControlMethod()
}
This question already has answers here:
Getting a "This application is modifying the autolayout engine from a background thread" error?
(21 answers)
Closed 6 years ago.
I am almost done migrating an iOS app of mine to Swift 3.0.
But I still have a few cases similar to the one below.
Most of them I have been able to solve by putting the problematic code on the main thread.
In some other cases, I can't figure out, which part of my code is executing on the wrong thread. And I get a message like this one:
This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0 CoreFoundation 0x000000018765a1d8 <redacted> + 148
1 libobjc.A.dylib 0x000000018609455c objc_exception_throw + 56
2 CoreFoundation 0x000000018765a108 <redacted> + 0
3 Foundation 0x0000000188241ea4 <redacted> + 192
....................
16 libsystem_pthread.dylib 0x00000001866eece4 <redacted> + 200
17 libsystem_pthread.dylib 0x00000001866ee378 pthread_mutex_lock + 0
18 libsystem_pthread.dylib 0x00000001866edda4 start_wqthread + 4
)
Is there some special technic (option when using the debugger or ??) I can use to trace the path followed by the progran, to see where this is happening?
Obviously you are doing some UI update on back ground thread. Cant predict exactly where, without seeing your code.
These are some situations it might happen:-
you might be doing something on background thread and not using. Being in the same function this code is easier to spot.
DispatchQueue.main.async { // do UI update here }
calling a func doing web request call on background thread and its completion handler calling other func doing ui update.
to solve this try checking code where you have updated the UI after webrequest call.
// Do something on background thread
DispatchQueue.global(qos: .userInitiated).async {
// update UI on main thread
DispatchQueue.main.async {
// Updating whole table view
self.myTableview.reloadData()
}
}
I don't think there is any other inbuilt tool for debugging such crashes because it's the code which is modifying the AutoLayout UI elements/constraints from the code which is either running in Background thread or completion handlers. All completion handlers by default run in background thread. You need to use the GCD to update the UI elements from your completion handler blocks.
I just received my first crash report from Crashlytics and am attempting to correct the issue. Unfortunately it is only with a line of code that runs on older devices so I can't test it on my iPhone 6.
The crash report from Crashlytics highlights two threads, the first reads:
Fatal Exception: NSInvalidArgumentException
-[CABasicAnimation altitude]: unrecognized selector sent to instance 0x17734440
While the second reads:
Crashed: Map Update :: NSOperation 0x1a839470
SIGABRT ABORT at 0x316a3dfc
The indicated line of code for both threads is:
let relativeAlt = mylocation.altitude - appDelegate.elevation
Where:
let mylocation = self.mapView.myLocation
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
I'm trying to understand what I'm reading in the crash report. The way I see it the program doesn't understand the altitude reference made for some reason? This doesn't make sense to me since this crash seems to occur after that app has been running for several minutes without error, the highlighted line of code is run possibly hundreds of times before the app crashed. What is really happening here?
Additional Information:
Since writing, I have received additional crashes that I believe stem from the same issue:
Crashed: Map Update :: NSOperation 0x19fb2d50
EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x11d077ca
Crashed: Map Update :: NSOperation 0x145ced50
EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x81450a64
The first highlighted the following line in my code (I believe since I had worked on the app since this beta release and the line numbers have changed slightly):
self.lastLocation = (self.mapView.myLocation as CLLocation).coordinate
While the second crash just gave me:
libobjc.A.dylib
objc_msgSend + 5
The first of the new crashes (That provided a line of code) provided this report:
Thread : Crashed: Map Update :: NSOperation 0x19fb2d50
0 libobjc.A.dylib 0x3105c708 objc_release + 7
1 FlightTracker 0x000ba830 FlightTracker.MapViewController. (locationManager (FlightTracker.MapViewController) -> (Swift.ImplicitlyUnwrappedOptional<ObjectiveC.CLLocationManager>, didUpdateLocations : Swift.ImplicitlyUnwrappedOptional<Swift.Array<Swift.AnyObject>>) -> ()).(closure #1) (MapViewController.swift:168)
2 Foundation 0x244ce0fd __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 8
3 Foundation 0x24438fc5 -[NSBlockOperation main] + 148
4 Foundation 0x2442b845 -[__NSOperationInternal _start:] + 768
5 Foundation 0x244d0a57 __NSOQSchedule_f + 186
6 libdispatch.dylib 0x315ad5d9 _dispatch_queue_drain$VARIANT$mp + 948
7 libdispatch.dylib 0x315ad0a9 _dispatch_queue_invoke$VARIANT$mp + 84
8 libdispatch.dylib 0x315af0d3 _dispatch_root_queue_drain + 330
9 libdispatch.dylib 0x315b01fb _dispatch_worker_thread3 + 106
10 libsystem_pthread.dylib 0x31720e25 _pthread_wqthread + 668
Probably not your problem, but I just had a SIGABRT that was driving me nuts (that's how I ended up looking at this question) and I'll post my solution in case it helps some future S.O. spelunker.
In my (iPad, not that it matters) app, you can push a button that results in the creation of a not-full-screen UIViewController which contains a UITableView, and this viewController is presented via UIPopoverController.
In my case I had a screw-up in my loading of the tableView items which, at the time of creating the tableView cell I ended up trying to add a null value into a dictionary. (It's a long story, having to do with an infrastructure class that expects the data to be in a certain format.)
Anyway, attempting to access newViewController.view caused the SIGABRT on that line, with no clue that the problem was related to filling the tableView cell. Nothing tableView-related was evident in the stack trace, so it took me quite a while to narrow things down. I eventually just guessed "maybe it's the tableview" and disconnected the IBOutlet and delegate/dataSource to see if the crash went away.
...And it did. Which lead me down the path of finding the real problem.
Anyway, that's my story. Hope it's helpful to someone.
Due to the lack of a full/proper crash report and the lack of more code and architecture, the following is an assumption using the little bits of information that are available.
You are accessing a variable in a background thread (NSOperation queue) that got released on another thread and now isn't available any longer, so the pointer shows to some other random object in the memory. And that random object surely has no idea what to do with the altitude message which is then causing the crash.
You have to make sure that all variables used in the background thread, are available and not released in another thread.
Wow, I just had another "impossible to track down" solution to this that was driving me bonkers.
Earlier in the day, I'd done a major refactor as some of the objects in my game had names like RFCFoo and some were like RfcBar and I wanted to standardize the capitalization.
So I used XCode's Refactor->Rename... tool, and it worked great, except for one thing:
It failed to rename one specific .xib file, which remained as "RFCBlahBlah.xib" when I was trying to load it as "RfcBlahBlah.xib"
Again, I hope this proves useful for some future SO searcher.