2

I'm working on a framework and in order to ensure non blocking public methods, I'm using a NSOperationQueue that puts all the public method calls into an operation queue and returns immediately.

There is no relation or dependencies between different operations and the only thing that matters is that the operations are started in FIFO order that is in the same order as they were added to the queue.

Here is an example of my current implementation (sample project here):

@implementation Executor

-(instancetype) init {
    self = [super init];
    if(self) {
        _taskQueue = [[NSOperationQueue alloc] init];
        _taskQueue.name = @"com.d360.tasks";

    }
    return self;
}

-(void) doTask:(NSString*) taskName
{
    NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"executing %@", taskName);
    }];

    [self.taskQueue addOperation:operation];
}

I realised though that the order at which the operations are started is not necessarily the order at which they were added to the queue. For instance, if I call

[self.executor doTask:@"Task 1"];
[self.executor doTask:@"Task 2"];

Sometimes Task 2 is started after Task 1.

The question is how can I ensure a FIFO execution start?

I could achieve it using _taskQueue.maxConcurrentOperationCount = 1; but this would allow only 1 operation at once which I don't want. One operation should not block any other operation and they can run concurrently as long as they are started in the correct order.

I looked also into the NSOperationQueuePriority property which would work If I knew the priorities of the calls which I don't. In fact, even if I sent the earlier added operation to NSOperationQueuePriorityHigh and the second to NSOperationQueuePriorityNormal the order is not guaranteed neither.

[self.executor doTask:@"Task 1" withQueuePriority:NSOperationQueuePriorityHigh];
[self.executor doTask:@"Task 2" withQueuePriority:NSOperationQueuePriorityNormal];

Output is sometimes

executing Task 2
executing Task 1

Any ideas?

thanks, Jan

Jan
  • 7,444
  • 9
  • 50
  • 74
  • You said: "in LIFO order that is in the same order as they were added to the queue". This statement is contradictory - you said LIFO order but what you described is FIFO order. So which is it? – Jeff Loughlin May 03 '17 at 11:50
  • yes sorry it's FIFO (updated) – Jan May 03 '17 at 12:04
  • You are saying _One operation should not block any other operation and they can run concurrently as long as they are started in the correct order_ meaning ___the tasks are independent___. And then you want to have `FIFO` ordering which eventually is adding ___dependency___. To be honest, the concept `FIFO` itself is a conditional operation. So as long as your tasks are independent why should the ordering be the headache?? – nayem May 03 '17 at 12:45
  • Having an execution order, not is different from a dependency where an operation cannot start before it's dependency is *finished* imo. But I get your point, I should re-think my design and add dependencies where it's necessary – Jan May 03 '17 at 12:52

1 Answers1

1

When you create each task you could add a dependency on the previous task with NSOperation -addDependency. The complication is that dependencies aren't satisfied until the dependent task completes, which probably isn't what you want. You could work around that by creating another NSOperation inside each task, and make the next queued task depend on that. This inner task can just set a flag or something that says "hey, I've started!". Then when that inner task completes it will satisfy the dependency for the next task in the queue and allow it to start.

Seems like a convoluted way to do things, though, and I'm not sure the benefit is worth the extra complication - why does it matter what order the operations are started in, if they truly are independent operations? Once they've started, the OS decides which task gets CPU time, and you don't have much control over it anyway, so why not just queue them up and let the OS manage the start order?

Jeff Loughlin
  • 4,134
  • 2
  • 30
  • 47
  • Yes you are right, it seems to be an overkill. I think a solution would be to have a dedicated and serial queue for the tasks where the order matter. – Jan May 03 '17 at 12:55