Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Barrier operations in NSOperationQueue

How can we implement dispatch_barrier_async's equivalent behavior using NSOperationQueue or any user-defined data-structure based on NSOperationQueue?

The requirement is, whenever a barrier operation is submitted it should wait until all non-barrier operations submitted earlier finish their execution and blocks other operations submitted after that.

  • Non-barrier operations should be able to perform concurrently.
  • Barrier operations should execute serially.

NB: Not using GCD,as it doesn't provide(or atleast difficult) much access over the operations, like cancelling single operation, etc.

like image 334
TheCommonEngineer Avatar asked Mar 19 '14 14:03

TheCommonEngineer


People also ask

What is the difference between GCD and NSOperationQueue in IOS?

Grand Central Dispatch is a low-level C API that interacts directly with Unix level of the system. NSOperation is an Objective-C API and that brings some overhead with it. Instances of NSOperation need to be allocated before they can be used and deallocated when they are no longer needed.


2 Answers

This is more or less what jeffamaphone was saying, but I put up a gist that should, in rough outline, do what you ask.

I create a NSMutableArray of NSOperationQueues, which serves as a "queue of queues". Every time you add a BarrierOperation object, you tack a fresh suspended op queue on the end. That becomes the addingQueue, to which you add subsequent operations.

- (void)addOperation:(NSOperation *)op {
    @synchronized (self) {
        if ([op isKindOfClass:[BarrierOperation class]]) {
            [self addBarrierOperation:(id)op];
        } else {
            [[self addingQueue] addOperation:op];
        }
    }
}

// call only from @synchronized block in -addOperation:
- (void)addBarrierOperation:(BarrierOperation *)barrierOp {
    [[self addingQueue] setSuspended:YES];

    for (NSOperation *op in [[self addingQueue] operations]) {
        [barrierOp addDependency:op];
    }

    [[self addingQueue] addOperation:barrierOp];

    // if you are free to set barrierOp.completionBlock, you could skip popCallback and do that

    __block typeof(self) weakSelf = self;
    NSOperation *popCallback = [NSBlockOperation blockOperationWithBlock:^{
        [weakSelf popQueue];
    }];
    [popCallback addDependency:barrierOp];
    [[self addingQueue] addOperation:popCallback];
    [[self addingQueue] setSuspended:NO];

    NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
    [opQueue setSuspended:YES];

    [_queueOfQueues addObject:opQueue]; // fresh empty queue to add to
}

When one NSOperationQueue finishes, it gets popped and the next one starts running.

- (void)popQueue
{
    @synchronized (self) {
        NSAssert([_queueOfQueues count], @"should always be one to pop");
        [_queueOfQueues removeObjectAtIndex:0];

        if ([_queueOfQueues count]) {
            // first queue is always running, all others suspended
            [(NSOperationQueue *)_queueOfQueues[0] setSuspended:NO];
        }
    }
}

I might have missed something crucial. The devil's in the details.

This smells a bit like a homework assignment to me. If so, tell me what grade I get. :)


Addendum: Via abhilash1912's comment, a different but similar approach. That code is tested, so it already wins. But it is a bit stale (2 years or so as of today; some deprecated method usage). Moreover, I question whether inheriting from NSOperationQueue is the best path, though it has the virtue of retaining familiarity. Regardless, if you've read this far, it's probably worth looking over.

If you create or find the world's greatest BarrierQueue class, please let us know in the comments or otherwise, so it can be linked up.

like image 112
Clay Bridges Avatar answered Sep 21 '22 14:09

Clay Bridges


Create an NSOperation that is your barrier, then use:

- (void)addDependency:(NSOperation *)operation

To make that barrier operation dependent on all the ones you want to come before it.

like image 35
i_am_jorf Avatar answered Sep 22 '22 14:09

i_am_jorf