1

My library exposes 2 APIs as follows to:

-(void) createFile{ 

    dispatch_sync(queueSerial, ^{ //B1

     [fileObj createFileInfo:file completion:^(NSError *error){

         //execute completion block C1
     }]; 

    });
}

-(void) readFile:(NSData*)timeStamp{ 

    dispatch_async(queueSerial, ^{ //B2

        [fileObj readFileInfo:fileName completion:^(NSError *error) {
            dispatch_async(queueSerial2, ^{

                //execute completion block C2

            });
         }]

    });
}

Both readFile and createFile are asynchronous methods.

I usually recommend to the people using my library to call createFile before readFile. However, there is no guarantee how the callers will end up implementing this. It usually gets invoked in the following fashion (and I have no control over this)

[fileClass createFile];
[fileClass readFile:timeStamp]; 

What I want to do is to ensure readFile gets called after the completion block C1 is executed. I also don't want to block the main thread with createFile (but this expectation can be relaxed). So what I want to achieve as end result is :

  1. Caller (that I have no control over) calls createFile and immediately after calls readFile
  2. createFile fully executes, completion block C1 gets fired and after that, readFile is dispatched to do it's thing.

How can I achieve this?

ExceptionHandler
  • 213
  • 1
  • 8
  • 24
  • @Rob thanks for the insight. I am a total fledgling (in Obj C) at this point so I will try to follow your suggestion henceforth. – ExceptionHandler Feb 22 '17 at 23:44
  • How does `readFile` pass back the data it read? It's defined to return `void`, so it must be using a completion handler or delegate pattern? – Rob Feb 23 '17 at 00:17
  • @Rob `readFile` passes the data (error value if any) in the completion handler C2. I wasn't sure if those parts were relevant. – ExceptionHandler Feb 23 '17 at 00:20

1 Answers1

1

It feels like you're trying to take an existing completion-handler based API and shoe-horn it into a serial queue pattern. But serial queues only make sense when the dispatched blocks are, themselves, synchronous. (And don't confuse this with dispatch_sync ... we're talking about whether the task inside the block is asynchronous or not, not how you dispatched it to your queue.) But you're dealing with asynchronous methods being called from other asynchronous methods.

The basic pattern when dealing with methods that take completion handler blocks, is to eliminate the serial queue altogether (no GCD queue is needed or is useful when the tasks are already asynchronous) and just use completion handler blocks in your own methods, and make the last call in the most deeply nested block call your own completion handler.

For example:

- (void)foo:(void (^)())completion {
    [fileObj setFileInfo:file completion:^(NSError *error) {
        // do some other stuff
        completion();
    }];
}

- (void)bar:(void (^)())completion {
    [fileObj getFileInfo:fileName completion:^(NSError *error) {
        // do some other stuff
        completion();
    }];
}

And then you'd call it like so:

[self foo:^{
    [self bar:^{
        // do whatever you want when its all done
    }];
}];

This means that it won't do any of the getFile stuff until the setFile stuff is done.

Having said this, I wonder what added value your setFile and getFile methods add above and beyond the FileObject classes' own methods.

But the key is that when dealing with asynchronous methods, you can't easily coordinate them with a simple serial GCD queue. If you want dispatch queue like behavior for tasks that are, themselves, asynchronous, then you'd generally consider using operation queues instead (or promises/futures or something like that).

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • I understand what you are trying to say but the thing is I only have control over the `setFile` `getFile` methods and *not* over how they get invoked unfortunately. So, I need to work around the limitation. The best I can do is to force the dispatch of`getFileInfo` after `setFileInfo` has finished – ExceptionHandler Feb 22 '17 at 23:43
  • Then you should edit your question share how they're getting called (and explain why they can't be changed). But the fundamental truth here is that you can't just take asynchronous methods and add them to a queue and expect the queue to handle that. You can, theoretically, make them synchronous, but that's a truly horrible idea (for lots of reasons). If you want, you can make these asynchronous `NSOperation` subclasses, but that's changing your API, too (and while kind of cool, takes more work to do properly). Please just edit the question entailing what constraints you have on your API... – Rob Feb 22 '17 at 23:49
  • as far as what value these methods add, they are the only methods of this class `FileObject` that is exposed to the clients calling into my process. I am not completely unopposed to use other methodologies to achieve this. It's just that the `setFile` method is computation heavy and needs to be performed before `getFile` gets called with it's time sensitive `timeStamp` expires – ExceptionHandler Feb 22 '17 at 23:49
  • That's what my `foo`/`bar` example does (I renamed them, though, because you shouldn't be naming non-accessor methods with the `set` prefix ... you'll introduce weird warnings if you start your non-accessor methods with `set`). But the above will call `bar` only when the computationally intensive `foo` is done, but not block the calling thread to achieve that. – Rob Feb 22 '17 at 23:51
  • I updated my question clarifying the constraints. Is it a good idea to use a lock and unlock inside the completion block `C1`? – ExceptionHandler Feb 23 '17 at 00:00
  • The road that you're going down is the all-too-common "how do I make these asynchronous methods behave synchronously." There are tons of techniques to make these guys behave synchronously (I'd probably use semaphores before locks), but it's almost always a really, really bad idea. The `FileObject` authors made their API asynchronous for a reason, presumably, and you're just re-introducing the problems that the they were trying to solve when they made their API asynchronous. – Rob Feb 23 '17 at 00:10
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/136405/discussion-between-exceptionhandler-and-rob). – ExceptionHandler Feb 23 '17 at 00:21