objective-cmacosdispatch-async

Is there any issue to call dispatch_semaphore_signal in the block after dispatch_release(no ARC case)?


  1. does the following code has some issue when timeout (no ARC case)?

  2. how about the dispatch_semaphore_signal is called after dispatch_release(semaphore)? I know a dispatch object is asynchronously deallocated once all references to it are released (the reference count becomes zero), so the reference of the semaphore is not 0 in the following code?

  3. is it necessary to add __block for semaphore?

         dispatch_time_t timeout = DISPATCH_TIME_FOREVER;
         if (waitTime > 0)
         {            
             timeout = dispatch_time(DISPATCH_TIME_NOW, waitTime * NSEC_PER_SEC);
         }
         dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);  
         dispatch_barrier_async(_dispatchQueue, ^{
             dispatch_semaphore_signal(semaphore);
         });
    
         dispatch_semaphore_wait(semaphore, timeout);
         dispatch_release(semaphore);
    

thanks a lot!


Solution

  • Based on comments.

    I think you need something like this. The done variable needs to be synced / atomic and there are two places in the code that need to be synced on some common lock. In fact, since you only access done inside those pieces it does not need to be synced.

    done = NO
    
    dispatch_barrier ...
       stuff
       sync {
         if(!done)
           signal
           done = YES;
       }
    
    wait with timeout
    cancel block
    sync {
      if( ! done )
        signal
        done = YES
    }
    
    // You can even test the logic as below
    if DEBUG
    {
      assert done == YES
    }
    
    release
    

    I am not sure what the impact of cancel block will be in non ARC environment but think it will be fine.

    This will balance the signals and waits and will cause the release to work and also prevent you from signalling the released semaphore.

    Here is an outline implementation in Objective-C

        __block BOOL done = NO;
        NSObject * lock = NSObject.new;
        dispatch_semaphore_t s = dispatch_semaphore_create ( 0 );
        dispatch_queue_t queue = dispatch_queue_create( "bak",
                                   dispatch_queue_attr_make_with_qos_class( DISPATCH_QUEUE_CONCURRENT,
                                                       QOS_CLASS_DEFAULT,
                                                       DISPATCH_QUEUE_PRIORITY_DEFAULT ) );
    
        dispatch_block_t block = dispatch_block_create ( DISPATCH_BLOCK_BARRIER, ^ {
    
            // Change this time to determine which one fires first
            [NSThread sleepForTimeInterval:1];
    
            @synchronized ( lock ) {
    
                if ( ! done )
                {
                    done = YES;
                    dispatch_semaphore_signal ( s );
                    NSLog ( @"Inside fired" );
                }
    
            }
    
        } );
    
        // Start the block
        dispatch_async ( queue, block );
    
        // ... or change time here to determine which one fires first
        dispatch_semaphore_wait ( s, dispatch_time ( DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC ) );
    
        @synchronized ( lock ) {
    
            if ( ! done )
            {
                done = YES;
                dispatch_semaphore_signal ( s );
                NSLog ( @"Outside fired" );
            }
    
        }
    
        // Done, release stuff *only* if not ARC
        dispatch_release ( s );
        dispatch_release ( queue );
        dispatch_release ( block );
        lock.release;