@synchronized crashing with ARC

  • I have a function that looks essentially like this:

    static void *kMyVLFContext = &kMyVLFContext;
    Boolean myFunction(CFURLRef path)
    {
    CFDictionaryRef myDictionary = NULL;

    @synchronized(kMyVLFContext) {
      … work …
    }

    return myDictionary != NULL;
    }

    This function works fine with manual memory management. After compiling with ARC though, a crash occurs on line "@synchronized(kMyVLFContext) {". I wouldn't think this should be affected by ARC at all. Any hints as to what might be afoot here, or is it more likely something else, earlier on is causing a failure here (e.g. stomping kMyVLFContext)?

    Removing the lock causes the function to return successfully, but may of course cause issues on occasions when the function is called from several threads at once.

    -António

    ----------------------------------------------------
    It isn't so important to do great things,
    as to do what you do with great love.
    ----------------------------------------------------
  • On May 31, 2012, at 8:47 AM, Antonio Nunes wrote:

    > static void *kMyVLFContext = &kMyVLFContext;
    ...
    > @synchronized(kMyVLFContext) {

    Huh? I thought the parameter to @synchronized(…) had to be an object reference?

    —Jens
  • My guess is that you are crashing because kMyVLFContext is not an objective C object. According to the Apple documentation (https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Obj
    ectiveC/Chapters/ocThreading.html#//apple_ref/doc/uid/TP30001163-CH19-SW1
    ):

    The @synchronized() directive takes as its only argument any Objective-C object, including self. This object is known as a mutual exclusion semaphore or mutex. It allows a thread to lock a section of code to prevent its use by other threads. You should use separate semaphores to protect different critical sections of a program. It’s safest to create all the mutual exclusion objects before the application becomes multithreaded, to avoid race conditions.

    My guess is that ARC is trying to do a bit of memory management on your context pointer and the calls it expects aren't there. What does your stack trace say. Are you in an runtime call or NSOjbect call?

    Scott

    On May 31, 2012, at 8:47 AM, Antonio Nunes wrote:

    > I have a function that looks essentially like this:
    >
    > static void *kMyVLFContext = &kMyVLFContext;
    > Boolean myFunction(CFURLRef path)
    > {
    > CFDictionaryRef myDictionary = NULL;
    >
    > @synchronized(kMyVLFContext) {
    > … work …
    > }
    >
    > return myDictionary != NULL;
    > }
    >
    > This function works fine with manual memory management. After compiling with ARC though, a crash occurs on line "@synchronized(kMyVLFContext) {". I wouldn't think this should be affected by ARC at all. Any hints as to what might be afoot here, or is it more likely something else, earlier on is causing a failure here (e.g. stomping kMyVLFContext)?
    >
    > Removing the lock causes the function to return successfully, but may of course cause issues on occasions when the function is called from several threads at once.
    >
    > -António
    >
    > ----------------------------------------------------
    > It isn't so important to do great things,
    > as to do what you do with great love.
    > ----------------------------------------------------
  • It looks a bit suspicious to me that you're instantiating the dictionary in your hidden "… work ..." code, then using it in the visible code, but not releasing it.

    Is it possible that the dictionary started out as an ObjC object that you don't own, and you used a (__bridge CFDictionaryRef) cast to assign it to myDictionary, expecting it to stick around until the autorelease pool pops?

    That's not what is guaranteed to happen under ARC - the system is now free to release the dictionary when the last strong pointer referring to it goes out of scope.  A CF reference is not an ARC-managed pointer, so it doesn't count as a strong pointer.

    If this is the case, there are a few solutions.  Making myDictionary an 'NSDictionary *' is the easiest one, since that will be fully managed by ARC, so it will keep a reference to it until you test it and the function exits.

    Jamie.

    On Thursday, 31 May 2012 at 16:47, Antonio Nunes wrote:
    > I have a function that looks essentially like this:
    >
    > static void *kMyVLFContext = &kMyVLFContext;
    > Boolean myFunction(CFURLRef path)
    > {
    > CFDictionaryRef myDictionary = NULL;
    >
    > @synchronized(kMyVLFContext) {
    > … work …
    > }
    >
    > return myDictionary != NULL;
    > }
    >
    > This function works fine with manual memory management. After compiling with ARC though, a crash occurs on line "@synchronized(kMyVLFContext) {". I wouldn't think this should be affected by ARC at all. Any hints as to what might be afoot here, or is it more likely something else, earlier on is causing a failure here (e.g. stomping kMyVLFContext)?
    >
    > Removing the lock causes the function to return successfully, but may of course cause issues on occasions when the function is called from several threads at once.
    >
    > -António
    >
    > ----------------------------------------------------
    > It isn't so important to do great things,
    > as to do what you do with great love.
    > ----------------------------------------------------
  • On 31 May 2012, at 17:14, Scott A Andrew wrote:

    > My guess is that you are crashing because kMyVLFContext is not an objective C object. According to the Apple documentation (https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Obj
    ectiveC/Chapters/ocThreading.html#//apple_ref/doc/uid/TP30001163-CH19-SW1
    ):
    >
    > The @synchronized() directive takes as its only argument any Objective-C object, including self. This object is known as a mutual exclusion semaphore or mutex. It allows a thread to lock a section of code to prevent its use by other threads. You should use separate semaphores to protect different critical sections of a program. It’s safest to create all the mutual exclusion objects before the application becomes multithreaded, to avoid race conditions.
    >
    > My guess is that ARC is trying to do a bit of memory management on your context pointer and the calls it expects aren't there.

    Ok, that seems to make sense. At any rate, changing kMyVLFContext to a more regular style (an NSString) seems to fix the issue.
    static NSString *kMyVLFContext = @"kMyVLFContext";

    Thanks,
    António

    > Scott
    >
    > On May 31, 2012, at 8:47 AM, Antonio Nunes wrote:
    >
    >> I have a function that looks essentially like this:
    >>
    >> static void *kMyVLFContext = &kMyVLFContext;
    >> Boolean myFunction(CFURLRef path)
    >> {
    >> CFDictionaryRef myDictionary = NULL;
    >>
    >> @synchronized(kMyVLFContext) {
    >> … work …
    >> }
    >>
    >> return myDictionary != NULL;
    >> }
    >>
    >> This function works fine with manual memory management. After compiling with ARC though, a crash occurs on line "@synchronized(kMyVLFContext) {". I wouldn't think this should be affected by ARC at all. Any hints as to what might be afoot here, or is it more likely something else, earlier on is causing a failure here (e.g. stomping kMyVLFContext)?
    >>
    >> Removing the lock causes the function to return successfully, but may of course cause issues on occasions when the function is called from several threads at once.
    >>
    >> -António
    >>
    >> ----------------------------------------------------
    >> It isn't so important to do great things,
    >> as to do what you do with great love.
    >> ----------------------------------------------------
    >

    -----------------------------------------------------------
    And could you keep your heart in wonder
    at the daily miracles of your life,
    your pain would not seem less wondrous
    than your joy.

    --Kahlil Gibran
    -----------------------------------------------------------
  • On 31 May 2012, at 17:00, Jens Alfke wrote:

    > Huh? I thought the parameter to @synchronized(…) had to be an object reference?

    Well, yes, but my understanding was/is that it was essentially about the pointer, and this formula worked just fine until I migrated to ARC. I think Scott is right that ARC expects a memory managed object and that is not what I was supplying. No trouble changing it to a more traditional reference, and that appears to work.

    -António

    -----------------------------------------
    Accepting others as they are
    brings a wonderful freedom
    to your own mind.

    --The Peace Formula
    -----------------------------------------
  • Le 31 mai 2012 à 18:00, Jens Alfke a écrit :

    >
    > On May 31, 2012, at 8:47 AM, Antonio Nunes wrote:
    >
    >> static void *kMyVLFContext = &kMyVLFContext;
    > ...
    >> @synchronized(kMyVLFContext) {
    >
    > Huh? I thought the parameter to @synchronized(…) had to be an object reference?

    The Objective-C reference agree with you.

    «The @synchronized() directive takes as its only argument any Objective-C object, including self.»

    http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Obje
    ctiveC/Chapters/ocThreading.html%23//apple_ref/doc/uid/TP30001163-CH19-SW1


    I'm surprised the compiler does not complain when passing an arbitrary pointer.

    -- Jean-Daniel
  • On May 31, 2012, at 2:00 PM, Jean-Daniel Dupas <devlists...> wrote:

    > Le 31 mai 2012 à 18:00, Jens Alfke a écrit :
    >
    >> On May 31, 2012, at 8:47 AM, Antonio Nunes wrote:
    >>
    >>> static void *kMyVLFContext = &kMyVLFContext;
    >> ...
    >>> @synchronized(kMyVLFContext) {
    >>
    >> Huh? I thought the parameter to @synchronized(…) had to be an object reference?
    >
    >
    > The Objective-C reference agree with you.
    >
    > «The @synchronized() directive takes as its only argument any Objective-C object, including self.»
    >
    > http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Obje
    ctiveC/Chapters/ocThreading.html%23//apple_ref/doc/uid/TP30001163-CH19-SW1

    >
    > I'm surprised the compiler does not complain when passing an arbitrary pointer.

    That's because the compiler can't know it's invalid, due to the use of void * as the var type (void * pretty much matches everything, including id, must like id matches any object reference).

    Suggestions:

    1. remove the k prefix, by convention that is reserved for constants

    2. don't use the same var name for a local variable as for a global variable

    3. That should be something like:

    static VLFContext* myVLFContext = nil;
    ...
    if( !myVLFContext )
      myVLFContext = [VLFContext alloc] init];
    ...
    @synchronized( myVLFContext ) {
    ...
    }

    -lane
  • Le 31 mai 2012 à 22:28, Lane Roathe a écrit :

    >
    > On May 31, 2012, at 2:00 PM, Jean-Daniel Dupas <devlists...> wrote:
    >
    >> Le 31 mai 2012 à 18:00, Jens Alfke a écrit :
    >>
    >>> On May 31, 2012, at 8:47 AM, Antonio Nunes wrote:
    >>>
    >>>> static void *kMyVLFContext = &kMyVLFContext;
    >>> ...
    >>>> @synchronized(kMyVLFContext) {
    >>>
    >>> Huh? I thought the parameter to @synchronized(…) had to be an object reference?
    >>
    >>
    >> The Objective-C reference agree with you.
    >>
    >> «The @synchronized() directive takes as its only argument any Objective-C object, including self.»
    >>
    >> http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Obje
    ctiveC/Chapters/ocThreading.html%23//apple_ref/doc/uid/TP30001163-CH19-SW1

    >>
    >> I'm surprised the compiler does not complain when passing an arbitrary pointer.
    >
    > That's because the compiler can't know it's invalid, due to the use of void * as the var type (void * pretty much matches everything, including id, must like id matches any object reference).

    > -lane

    This is barely an excuse. Trying to send a message to a void * ptr result in the following warning:

    receiver type 'void *' is not 'id' or interface pointer, consider casting it to 'id'

    So, the compiler is perfectly able to complain when you use a void* instead of an objc object.

    -- Jean-Daniel
  • On May 31, 2012, at 3:28 PM, Lane Roathe wrote:

    > Suggestions:
    >
    > 1. remove the k prefix, by convention that is reserved for constants
    >
    > 2. don't use the same var name for a local variable as for a global variable
    >
    > 3. That should be something like:
    >
    > static VLFContext* myVLFContext = nil;
    > ...
    > if( !myVLFContext )
    > myVLFContext = [VLFContext alloc] init];
    > ...
    > @synchronized( myVLFContext ) {
    > ...
    > }

    You’ve got a potential race condition in the initializer. Using dispatch_once instead will solve that:

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    myVLFContext = [[VLFContext alloc] init];
    });

    @synchronized(myVLFContext) {
    ...
    }

    If you keep having trouble with @synchronized, though, I’d recommend just switching to a pthread_mutex_t, a spin lock, or dispatch_sync. Any of these should have better performance than @synchronized anyway.

    Charles
  • On 1 Jun 2012, at 02:34, Charles Srstka wrote:

    > static dispatch_once_t onceToken;
    > dispatch_once(&onceToken, ^{
    > myVLFContext = [[VLFContext alloc] init];
    > });
    >
    > @synchronized(myVLFContext) {
    > ...
    > }

    That does seem better. Any reason though to create a custom class? Couldn't we just [[NSObject alloc] init]? I think @synchronized cares about pointer uniqueness, not about the class, no? (And, as I learned from this, under ARC the pointer should also actually respond to memory management methods, i.e. be a real object, which is satisfied simply by it pointing to an object of class NSObject.)

    > If you keep having trouble with @synchronized, though, I’d recommend just switching to a pthread_mutex_t, a spin lock, or dispatch_sync. Any of these should have better performance than @synchronized anyway.

    I'd already solved the issue with @synchronized, but am indeed considering switching to pthread_mutex_t or dispatch_sync, although I think the practical performance gains will be marginal.

    -António

    ----------------------------------------------------
    Energy is like a muscle,
    it grows stronger through being used.
    ----------------------------------------------------
  • On May 31, 2012, at 10:59 PM, Antonio Nunes wrote:

    > On 1 Jun 2012, at 02:34, Charles Srstka wrote:
    >
    >> static dispatch_once_t onceToken;
    >> dispatch_once(&onceToken, ^{
    >> myVLFContext = [[VLFContext alloc] init];
    >> });
    >>
    >> @synchronized(myVLFContext) {
    >> ...
    >> }
    >
    > That does seem better. Any reason though to create a custom class? Couldn't we just [[NSObject alloc] init]? I think @synchronized cares about pointer uniqueness, not about the class, no? (And, as I learned from this, under ARC the pointer should also actually respond to memory management methods, i.e. be a real object, which is satisfied simply by it pointing to an object of class NSObject.)

    Indeed this is the case. The VLFContext thing being in there came from my basing my reply on the pseudocode snippet the previous person posted — just replacing the simple test against nil with dispatch_once.

    >> If you keep having trouble with @synchronized, though, I’d recommend just switching to a pthread_mutex_t, a spin lock, or dispatch_sync. Any of these should have better performance than @synchronized anyway.
    >
    > I'd already solved the issue with @synchronized, but am indeed considering switching to pthread_mutex_t or dispatch_sync, although I think the practical performance gains will be marginal.

    If it’s working with acceptable performance, then it might be good enough. If you’re having problems, or if performance leaves something to be desired, then it’s not a bad idea to look into the other methods.

    Charles
  • On May 31, 2012, at 8:59 PM, Antonio Nunes wrote:

    > Couldn't we just [[NSObject alloc] init]? I think @synchronized cares about pointer uniqueness, not about the class, no? (And, as I learned from this, under ARC the pointer should also actually respond to memory management methods, i.e. be a real object, which is satisfied simply by it pointing to an object of class NSObject.)

    In cases where I need a global lock for use by a particular class, I just use the class object:

    @synchronized([MyClass class]) { … }

    (don't use [self class] because it can have different values if there are any subclasses.)

    —Jens
previous month may 2012 next month
MTWTFSS
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
Go to today