4

I am working on an app that allows an user to create files and folders on the cloud from iPad. When a file is deleted, the app automatically creates a 'Recycle Bin' folder on the cloud and puts that file in it. I have created NSOperationQueue for all the operations i.e. I have separate queue for Folder Creation and a separate queue for File Upload. The problem that I am facing is that, the operation for file upload gets executed before the operation for folder creation is completed and so file doesn't gets uploaded successfully.

Can anyone help me to make the folder creation operation synchronous?

I have tried the following code

[create_folder_queue addOperations:[NSArray arrayWithObject:folderOperation] waitUntilFinished:YES];

but it doesn't execute the operation.

Thanks in advance.

Lorenzo B
  • 33,216
  • 24
  • 116
  • 190
Musti
  • 41
  • 2
  • 3
  • Show us more code (where are you calling the addOperations from, what type folderOperation is) – Danra Feb 13 '12 at 15:09

6 Answers6

11

If you want a synchronous queue, run the -setMaxConcurrentOperationCount: method on your operation queue to set it to 1.

After setting this property, you will only have at most one operation running at a time in the queue, which is more or less the definition of a synchronous queue, where one task only follows after another is complete.

Alex Reynolds
  • 95,983
  • 54
  • 240
  • 345
  • Hi Alex,Thanks for your reply, but I have two separate queues, one for Folder creation and other for file upload, and they execute simultaneously..setting concurrent operation on 1 wont make much difference.. – Musti Feb 07 '12 at 10:07
  • You might then want to read Apple's *Managing Concurrency* document, particularly the parts about dependencies: https://developer.apple.com/cocoa/managingconcurrency.html – Alex Reynolds Feb 07 '12 at 10:12
  • By synchronous, do you mean the thread runs on sequential order (one after another) or running on the main thread? – mr5 May 13 '16 at 05:07
  • 4
    This is the definition of a serial queue, not a synchronous one. The calling thread may finish executing before the synchronous operations are executed. – skooter Nov 16 '16 at 00:41
4

As far as I know one NSOperation object can depend on other and it is not mandatory for them to be executed in the same operation queue. So you can do next:

// create queues
NSOperationQueue *folder_creation_queue = [[NSOperationQueue alloc] init];
NSOperationQueue *file_uploading_queue  = [[NSOperationQueue alloc] init];
// create operations
NSInvocationOperation *folder_creation_operation = [[NSInvocationOperation alloc] 
                                                     initWithTarget:aFolderTarget 
                                                     selector:@selector(folderCreation) 
                                                     object:aFolderObject];
NSInvocationOperation *file_uploading_operation = [[NSInvocationOperation alloc] 
                                                     initWithTarget:aFileTarget 
                                                     selector:@selector(fileUpload) 
                                                     object:aFileObject];
// Set dependency for the folder_uploading_operation. 
// It prevents this operation begin executing before 
// folder_creation_operation finished.
[file_uploading_operation addDependency:folder_creation_operation];
// Execute these operations
[folder_creation_queue addOperation:folder_creation_operation];
[file_uploading_queue addOperation:file_uploading_operation];

Pay attention that there is no memory management in this example.

shim
  • 9,289
  • 12
  • 69
  • 108
slava
  • 377
  • 5
  • 11
  • can you apply this code for regular methods "like a get operation which pulls the data from the server". I need to pull data and in order to keep relations healthy, pulling operations should be sequential. – Zafer Fatih Koyuncu Nov 29 '13 at 20:19
2

You probably shouldn't use separate operation queues. As these are all cloud file operations, you should have one cloud file operation queues performing all cloud file operations.

If the operations don't depend on other operations, you just add them to this queue and they are performed in any order and as many of them at the same time as the system considers useful. Of course, you can set maxConcurrentOperationCount to place your own limit on this if you think that is a good idea, but you don't have to, in which case the system will decide for you.

In case operations do depend on each other, you use the operation dependency system. Therefor you first create the operations and then use addDependency to tell the system which operation depends on which other operation. If you now add all these operations to the queue, the system will make sure to run them in the correct order, so that an operation will only run after all operations it depends on have finished (and these may again depend on other operations and so on). Just make sure to not created cyclic dependencies (OpA depends on OpB which depends on OpC which depends on OpA) as then none of them will ever run.

But to the answer the question in general, in case other users ask how you can simulate a dispatch_sync with NSOperationQueue:

NSOperationQueue * queue = ...;
NSOperation * someOperation = [NSBlockOperation
    blockOperationWithBlock:^{
        // ... do whatever you want to do ...
    }
];
[queue addOperation:someOperation];
[someOperation waitUntilFinished];

Note however, that this far less performant than calling dispatch_sync on a serial dispatch queue since the code above always causes two thread switches whereas calling dispatch_sync on a serial dispatch queue will reuse the calling thread to run the block and thus no thread switch at all will take place, nor will the process require a second thread to do so. See here for details on how this works.

Mecki
  • 125,244
  • 33
  • 244
  • 253
1

This should solve your issue:

[create_folder_queue addOperation:folderOperation];
[create_folder_queue waitUntilAllOperationsAreFinished];

// ... now start file upload ...
Felix
  • 35,354
  • 13
  • 96
  • 143
  • Thanks for the help. I tried this, but by doing this, the operation doesn't gets executed. I don't know where I am wrong. – Musti Feb 10 '12 at 10:14
  • what is the execution status of folderOperation before and after `[create_folder_queue waitUntilAllOperationsAreFinished];`, i.e. what does `isCancelled`, `isExecuting`, `isFinished` and `isReady` return? – Felix Feb 10 '12 at 18:41
1

Along with setMaxConcurrentOperationCount, in your NSOperation subclass :

- (void)main
{    
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    [pool release];
}

- (BOOL)isConcurrent {
    return FALSE;
}

Now it's in a completely serial sequence mode.

shim
  • 9,289
  • 12
  • 69
  • 108
Sanjeev Rao
  • 2,247
  • 1
  • 19
  • 18
0

Why do you have two queues? If you can have only one, then your problem is solved with dependencies (see the documentation).

Vladimir Gritsenko
  • 1,669
  • 11
  • 25
  • The reason for having two queues is that, i dont want the user to sit idle when 1 operation is in progress.For example, if I have 1 queue, then if users uploads a large file, then he/she has to wait until the file is uploaded in order to perform any other operation(such as creating folder, download a file, etc). – Musti Feb 07 '12 at 10:14