Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSOperationQueue serial FIFO queue

Is it possible to use an NSoperationQueue object as a serial FIFO queue by setting its maxConcurrentOperationCount to 1?

I note that the docs state...

For a queue whose maximum number of concurrent operations is set to 1, this equates to a serial queue. However, you should never rely on the serial execution of operation objects.

Does this mean that FIFO execution is not guaranteed?

like image 578
Barjavel Avatar asked Jun 08 '12 12:06

Barjavel


2 Answers

In most cases, it will be FIFO. However, you can set up dependencies between NSOperations such that an operation submitted early will let other operations pass it by in the queue until its dependencies are satisfied.

This dependency management is why the docs indicate that FIFO-ness cannot be guaranteed. If you're not using dependencies, though, you should be fine to rely on it.

Update: NSOperation also has a queuePriority property, which can also cause operations to execute in non-FIFO order. The highest-priority operation with no pending dependencies will always execute first.

An NSOperation subclass might also override -isReady, which could cause it to move back in the queue.

So execution on your queue is guaranteed to be serial, in that no more than one operation will run at a time in this queue. But Apple can't guarantee FIFO; that depends on what you're doing with the operations you put in.

like image 83
BJ Homer Avatar answered Oct 09 '22 03:10

BJ Homer


The queue is not FIFO as mentioned by the documentation. You can make it strictly FIFO if you ensure that any new operation depends on the last operation added in the queue and that it can only run a single operation at a time. Omar solution is correct, but more generally, you can do the following:

NSOperationQueue* queue = [[ NSOperationQueue alloc ] init];
queue.maxConcurrentOperationCount = 1;

NSOperation* someOperation = [ NSBlockOperation blockOperationWithBlock:^(void) { NSLog(@"Done.");} ];

if ( queue.operations.count != 0 )
    [ someOperation addDependency: queue.operations.lastObject ];

This works because queue.operations is an array: whatever you add is not reordered (it is not a NSSet for instance). You can also simply add a category to your NSOperationQueue:

@interface NSOperationQueue (FIFOQueue)
- (void) addOperationAfterLast:(NSOperation *)op;
@end

@implementation NSOperationQueue (FIFOQueue)

- (void) addOperationAfterLast:(NSOperation *)op
{
    if ( self.maxConcurrentOperationCount != 1)
        self.maxConcurrentOperationCount = 1;

    NSOperation* lastOp = self.operations.lastObject;
    if ( lastOp != nil )
        [ op addDependency: lastOp ];

    [ self addOperation:op];
}

@end

and use [queue addOperationAfterLast:myOperation]. queuePriority has nothing to do with FIFO, it is related to job scheduling.

Edit: following a comment below, suspending the queue if checking for the count is also not sufficient. I believe this form is fine (upon testing, this does not create a race condition and does not crash).

Some info: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOperationQueue_class/#//apple_ref/occ/instp/NSOperationQueue/suspended

like image 22
Daniel Avatar answered Oct 09 '22 04:10

Daniel