1

I probably ask my question the wrong way and risk being blocked by stackoverflow completely. I have Asperger and no social skills, so I am very sorry for asking my last (?) question (because systems like these are only made for people without handicaps).

I am using GCD to load images and video from Instagram. I do this in an app that is very 'busy' with its user interface, and I want to keep that running smoothly, so I load the Instagram-media in the background.

The code below (which I probably formatted the wrong way so I apologize up front) works fine and does exactly want I want it to do. It loads images in the background (I left video's out to keep things simple) and my main UI is response while the images load. I display them between loads, and there works fine too.

However.

After 10 minutes, sometimes 20 minutes, sometimes 30 minutes and even sometimes after two hours my app gets OSSpinLockLock freezes. If I remove the code below I get no media, but the app never freezes.

I have searched for days on the web about alternative ways to do the job and to find an explanation for the OSSpinLockLock. No luck. Only thing I found was that using GCD could not result in an OSSpinLockLock. I have used all my knowledge of Instruments (which I must admit is more limited than I thought), but I cannot find a fault.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
    dispatch_async(queue, ^(void) {
        [[InstagramEngine sharedEngine] getMediaAtLocation:location count:kInstagramLocationBufferSize maxId:nil withSuccess:^(NSArray* media, InstagramPaginationInfo* paginationInfo) {
            if (media && media.count>0) {
                for (InstagramMedia* mediaObject in media) {
                    NSData* data = [[NSData alloc] initWithContentsOfURL:mediaObject.standardResolutionImageURL];
                    if (data) {
                        UIImage* img = [UIImage imageWithData:data];
                        if (img)
                            [self.locationBuffer addObject:img];
                        data = nil;
                    }
                }
            }
         } failure:^(NSError *error) {
             ;
         }];
    });

If you look at this code, do you see anything that might cause that lock? Because I most certainly don't.

self.locationBuffer is declared in the .h as

@property (nonatomic,strong) NSMutableArray* locationBuffer;

and is properly allocated and initialized (otherwise it would be rather clear what the problem was). I have also tried not to put the UIImage but the NSData in the array but that made no difference whatsoever.

On my iPad mini retina for instance the CPU-load goes to 195% and stays around that number for a very long time. Eventually, sometimes after several hours, the app crashes.

Any suggestions would be very welcome.

Edit: As I see now on the ips-file on the iPad itself (which for some mysterious reason I cannot paste into this webpage (is stackoverflow still in an experimental stage?)) I see that the iPad did spent 16.000+ seconds on NSURLConnection...

  • Don't know but maybe this could be of some help: http://stackoverflow.com/questions/19570388/i-get-osspinlocklock-when-calling-startdevicemotionupdatestoqueue-inside-a-contr – Peter Segerblom Jan 21 '15 at 14:49
  • You should post the actual backtrace. Just knowing that you hang on `OSSpinLockLock` doesn't give us any substantive clue as to what's going on -- spin locks are used all over the place in the OS and frameworks. – ipmcc Jan 21 '15 at 15:20
  • Ok. I will soon post a trace from Instruments, maybe you can get more out of it than I do. –  Jan 21 '15 at 15:33
  • I als have the first iPad mini model and that just completed a 5-hour session. Same code, no problem at all. That machine is running iOS 7 instead of 8. Can either one of these two 'parameters' have anything to do with it? –  Jan 21 '15 at 15:45
  • Impossible to rule it out, but there's really not enough info here. – ipmcc Jan 21 '15 at 15:49
  • Well, since my app displays a MKMapView all the time I am pretty sure that this causes a lot of trouble in the Retina-version. The map is displayed using more pixels and leaks memory all over the place. I solved that by throwing away the overlays and the map itself every ten minutes. This is not required on the non-retina version. But, this only explains memory-issues (that I not talked about here before). The other difference is iOS 7 and 8. How can exactly the same code run perfectly for 100% of the time, for 5 to 6 hours straight, on iOS 7 non-retina and always freeze in iOS 8 retina? –  Jan 21 '15 at 16:04
  • The system frameworks have changed, so even if *your* code didn't change, the code that's *running* (i.e. the system code called by your code) is different. It's not surprising to me that there are differences. – ipmcc Jan 21 '15 at 16:25
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/69328/discussion-between-ipmcc-and-michel). – ipmcc Jan 21 '15 at 16:37
  • Sorry, can't chat. Asperger. I tried to paste an excerpt from the ips-file from my iPad but this site doesn't allow that (sounds pretty stupid to me). It mentions a thread that execute NSURLConnection for 16.000 secs. (Sorry for my typo's, but you can't seriously type on an iPad). –  Jan 21 '15 at 16:50
  • The very first paragraph, where you accuse StackOverflow of being "only made for people without handicaps", shows your lack of social skills very well. Get rid of it. – gnasher729 Jan 27 '16 at 09:06

2 Answers2

0

my tought is that some timeout or failure blocks your gcd queues for too much time. try rewriting that code with NSOperationQueue, that way you can stop the queue on errors or view/controller going away.

https://developer.apple.com/library/ios/documentation/Cocoa/Reference/NSOperationQueue_class/index.html

Update 1:

Here is my trial at your code, i added queue and logs, check them and also check the timeout value. This way if a request does not finish (most likely) you can trace it. All requests are serial so if one of them stops you should immediately notice it. You can create more than one queue and access them sequentially (round robin) to have more requests simultaneously. I would not go with more than 4 queues which is also the default for most desktop internet browsers.

// keep the same queue for all request (class member?), don't put it in the same block
NSOperationQueue* queue= [[NSOperationQueue alloc] init];

// keep this in another code block from queue
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^(void) {
 [[InstagramEngine sharedEngine] getMediaAtLocation:location count:kInstagramLocationBufferSize maxId:nil withSuccess:^(NSArray* media, InstagramPaginationInfo* paginationInfo) {
     if (media && media.count>0) {
         for (InstagramMedia* mediaObject in media) {
             NSURL* url= mediaObject.standardResolutionImageURL;
             NSLog(@"Start loading from %@", url);
             NSURLRequest* req= [NSURLRequest requestWithURL:mediaObject.standardResolutionImageURL
                                                                                     cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                                             timeoutInterval:30]; // 30 seconds timeout
             [NSURLConnection sendAsynchronousRequest:req queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                 NSLog(@"Stop loading from %@ %@ %@", url, response, connectionError);
                 if (data != nil && connectionError == nil) {
                     UIImage* img = [UIImage imageWithData:data];
                     if (img) {
                         // if you are triggering some ui update run on main thread
                         // [self.locationBuffer addObject:img];
                         [self.locationBuffer performSelectorOnMainThread:@selector(addObject:)
                                                                                                     withObject:img
                                                                                                waitUntilDone:NO];
                     }
                 }
             }];
         }
     }
 } failure:^(NSError *error) {
     NSLog(@"Error getting media list: %@", error);
 }];
});
Gianluca P.
  • 1,536
  • 15
  • 15
  • Thanks for that tip. I am not familiar with it but I found a tutorial from Ray Wenderlich. Thank you. –  Jan 21 '15 at 15:36
  • Worked it through, and though it is a different approach it is no explanation for what is happening in my app. Thanks anyway. –  Jan 21 '15 at 15:58
  • I think some process is stopping the queue (DISPATCH_QUEUE_PRIORITY_DEFAULT) for too much time and os throws a spin lock. If you used a new `NSOperationQueue` it simply create a new thread used only for that "operation-queue" beside giving you more control. btw why downvote? – Gianluca P. Jan 22 '15 at 08:24
  • Sorry, I was not aware I did downvote. I do not understand the NSOperationQueue (my bad, I know) so I tried to not use `[[NSData alloc] initWithContentsOfURL:mediaObject.standardResolutionImageURL]` any more and use `NSURLConnectionDelegate` instead. I do not know if this will lead to a good solution, but at least I understand this stuff. –  Jan 22 '15 at 14:28
  • Gianluca: it seems you are right. `initWithContentsOfURL` sometimes blocks. I now use the `NSOperationQueue` and allocate a new queue each time. My first two tests went well, but the OSSpinLockLock occurred on the strangest of moments. I have to test more to see if it really is the solution. [I did not downvote, by the way, I even upvoted but someone else must have downvoted, making the balance 0]. –  Jan 22 '15 at 17:02
  • i updated the answer with a sample of what i would have tried ;) – Gianluca P. Jan 23 '15 at 09:28
  • Thanks Gianluca, this works well. In the meantime I have also a working version using NSOperationQueue. Since there is a lot more code going on than what I posted (I wanted to keep it short) I went for the `NSOperationQueue* queue = [[NSOperationQueue alloc] init];`-solution. Your code will come in handy somewhere, I'm sure. –  Jan 24 '15 at 20:49
  • There is a bug in iOS 8+ with OSSpinLock: http://openradar.appspot.com/23896366. See my answer here: http://stackoverflow.com/questions/30269243/application-sticks-on-osspinlocklockslow?lq=1 – Petr Jan 27 '16 at 08:27
0

OSSpinLocks has bug on iOS8.3+: report
,which leads to such freezes.
I think you should replace OSSpinLocks with something else to fix this in your app (maybe create your own realization of spin lock - search SO for OSSpinLock).

Petr
  • 836
  • 1
  • 14
  • 37