Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I get OSSpinLockLock when calling startDeviceMotionUpdatesToQueue inside a controller

In my root controller I have a property with the CMMotionManager

@property (strong, nonatomic) CMMotionManager *MManager;

In its getter I do lazy instantiation. When the controller's view loads, I call this method

- (void)reloadAccelerometer {
    NSLog(@"Away we go");
    self.MManager.deviceMotionUpdateInterval = 10.0/60.0;
    [self.MManager startDeviceMotionUpdatesToQueue:self.queue withHandler:^(CMDeviceMotion *motion, NSError *error) {
        NSLog(@"Y values is: %f", motion.userAcceleration.y);
    }];
}

I see "Away we go" in the NSLog and then immediately the app crashes and I get this thread log

libsystem_platform.dylib`spin_lock$VARIANT$mp:
0x39a87814:  movs   r1, #1

libsystem_platform.dylib`OSSpinLockLock$VARIANT$mp + 2:
0x39a87816:  ldrex  r2, [r0]
0x39a8781a:  cmp    r2, #0
0x39a8781c:  it     ne
0x39a8781e:  bne.w  0x39a893ec                ; _OSSpinLockLockSlow$shim
0x39a87822:  strex  r2, r1, [r0]
0x39a87826:  cmp    r2, #0
0x39a87828:  bne    0x39a87816                ; OSSpinLockLock$VARIANT$mp + 2
0x39a8782a:  dmb    ish
0x39a8782e:  bx     lr

What's my mistake? Did i place reloadAccelerometer in the wrong place?

like image 964
Morgan Wilde Avatar asked Oct 24 '13 15:10

Morgan Wilde


1 Answers

I was trying to do something similar in my iOS app and spent forever trying to figure out what the cause of the crash was. This was a very cryptic (and pesky) exception. I eventually figured it out after reading through the crash reports that OSSpinLock has to do with a thread / queue management issue.

The NSOperationQueue is the culprit here. Your code doesn't show how you were creating your NSOperationQueue, but I assume it was something like this:

NSOperationQueue *aQueue = [[NSOperationQueue alloc] init]; // Do NOT do this
[self.MManager startDeviceMotionUpdatesToQueue:aQueue withHandler:^(CMDeviceMotion *motion, NSError *error) {
    NSLog(@"Y values is: %f", motion.userAcceleration.y);
}];

It turns out that this is not the way to use the NSOperationQueue. That aQueue object is the cause of the crash.

To properly setup the operation queue and avoid a crash, you should move your CMMotionManager to a different thread. Then tell NSOperationQueue to use the currentQueue, NOT the mainQueue. Apple recommends that it is not run on the mainQueue, however if your app is currently running in the main queue then I don't see how the currentQueue is any different. I tried moving the code below to a different queue using GCD, but no code was ever called.

Here's what your final code should look like:

// Check if Motion / Location services are available
if (motionManager.deviceMotionAvailable == YES && motionManager.accelerometerAvailable == YES) {
    NSLog(@"Away we go");
    self.MManager.deviceMotionUpdateInterval = 10.0/60.0;
    [self.MManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
        NSLog(@"Y values is: %f", motion.userAcceleration.y);
     }];
} else {
    // Motion / Accelerometer services unavailable
}

I should also note that your creation of the CMMotionManager property (to my knowledge) is correct with (strong, nonatomic).

like image 61
Sam Spencer Avatar answered Dec 07 '22 22:12

Sam Spencer