Revision d8426ffb pithos-macos/PithosBrowserController.m

b/pithos-macos/PithosBrowserController.m
120 120
    return [super initWithWindowNibName:@"PithosBrowserController"];
121 121
}
122 122

  
123
- (void)dealloc {
124
    [[NSNotificationCenter defaultCenter] removeObserver:self];
125
    dispatch_release(moveQueue);
126
    dispatch_release(copyQueue);
127
    dispatch_release(deleteQueue);
128
    dispatch_release(uploadQueue);
129
    dispatch_release(downloadQueue);
130
    [moveNetworkQueue cancelAllOperations];
131
    [moveNetworkQueue release];
132
    [copyNetworkQueue cancelAllOperations];
133
    [copyNetworkQueue release];
134
    [deleteNetworkQueue cancelAllOperations];
135
    [deleteNetworkQueue release];
136
    [uploadNetworkQueue cancelAllOperations];
137
    [uploadNetworkQueue release];
138
    [downloadNetworkQueue cancelAllOperations];
139
    [downloadNetworkQueue release];
140
    [refreshTimer invalidate];
141
    [refreshTimer release];
142
    [clipboardParentNode release];
143
    [clipboardNodes release];
144
    [draggedParentNode release];
145
    [draggedNodes release];
146
    [sharedPreviewController release];
147
    [othersSharedNode release];
148
    [mySharedNode release];
149
    [sharedNode release];
150
    [containersNodeChildren release];
151
    [containersNode release];
152
    [accountNode release];
153
    [rootNode release];
154
    [pithos release];
155
    [super dealloc];
123
- (void)windowDidLoad {
124
    [super windowDidLoad];
125
    if (browser && !browserInitialized) {
126
        browserInitialized = YES;
127
        [self initBrowser];
128
    }
156 129
}
157 130

  
158 131
- (void)initBrowser {
......
172 145
    
173 146
    moveNetworkQueue = [[ASINetworkQueue alloc] init];
174 147
    moveNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
175
    [moveNetworkQueue go];
148
//    moveNetworkQueue.maxConcurrentOperationCount = 1;
176 149
    copyNetworkQueue = [[ASINetworkQueue alloc] init];
177 150
    copyNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
178
    [copyNetworkQueue go];
151
//    copyNetworkQueue.maxConcurrentOperationCount = 1;
179 152
    deleteNetworkQueue = [[ASINetworkQueue alloc] init];
180 153
    deleteNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
181
    [deleteNetworkQueue go];
154
//    deleteNetworkQueue.maxConcurrentOperationCount = 1;
182 155
    uploadNetworkQueue = [[ASINetworkQueue alloc] init];
183 156
    uploadNetworkQueue.showAccurateProgress = YES;
184 157
    uploadNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
185
    [uploadNetworkQueue go];
158
//    uploadNetworkQueue.maxConcurrentOperationCount = 1;
186 159
    downloadNetworkQueue = [[ASINetworkQueue alloc] init];
187 160
    downloadNetworkQueue.showAccurateProgress = YES;
188 161
    downloadNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
189
    [downloadNetworkQueue go];
162
//    downloadNetworkQueue.maxConcurrentOperationCount = 1;
163
    
164
    moveQueue = [[NSOperationQueue alloc] init];
165
    [moveQueue setSuspended:YES];
166
    moveQueue.name = @"gr.grnet.pithos.MoveQueue";
167
//    moveQueue.maxConcurrentOperationCount = 1;
168
    copyQueue = [[NSOperationQueue alloc] init];
169
    [copyQueue setSuspended:YES];
170
    copyQueue.name = @"gr.grnet.pithos.CopyQueue";
171
//    copyQueue.maxConcurrentOperationCount = 1;
172
    deleteQueue = [[NSOperationQueue alloc] init];
173
    [deleteQueue setSuspended:YES];
174
    deleteQueue.name = @"gr.grnet.pithos.DeleteQueue";
175
//    deleteQueue.maxConcurrentOperationCount = 1;
176
    uploadQueue = [[NSOperationQueue alloc] init];
177
    [uploadQueue setSuspended:YES];
178
    uploadQueue.name = @"gr.grnet.pithos.UploadQueue";
179
//    uploadQueue.maxConcurrentOperationCount = 1;
180
    downloadQueue = [[NSOperationQueue alloc] init];
181
    [downloadQueue setSuspended:YES];
182
    downloadQueue.name = @"gr.grnet.pithos.DownloadQueue";
183
//    downloadQueue.maxConcurrentOperationCount = 1;
190 184
    
191
    moveQueue = dispatch_queue_create("gr.grnet.pithos.MoveQueue", NULL);
192
    copyQueue = dispatch_queue_create("gr.grnet.pithos.CopyQueue", NULL);
193
    deleteQueue = dispatch_queue_create("gr.grnet.pithos.DeleteQueue", NULL);
194
    uploadQueue = dispatch_queue_create("gr.grnet.pithos.UploadQueue", NULL);
195
    downloadQueue = dispatch_queue_create("gr.grnet.pithos.DownloadQueue", NULL);
185
    moveCallbackQueue = [[NSOperationQueue alloc] init];
186
    [moveCallbackQueue setSuspended:YES];
187
    moveCallbackQueue.name = @"gr.grnet.pithos.MoveCallbackQueue";
188
//    moveCallbackQueue.maxConcurrentOperationCount = 1;
189
    copyCallbackQueue = [[NSOperationQueue alloc] init];
190
    [copyCallbackQueue setSuspended:YES];
191
    copyCallbackQueue.name = @"gr.grnet.pithos.CopyCallbackQueue";
192
//    copyCallbackQueue.maxConcurrentOperationCount = 1;
193
    deleteCallbackQueue = [[NSOperationQueue alloc] init];
194
    [deleteCallbackQueue setSuspended:YES];
195
    deleteCallbackQueue.name = @"gr.grnet.pithos.DeleteCallbackQueue";
196
//    deleteCallbackQueue.maxConcurrentOperationCount = 1;
197
    uploadCallbackQueue = [[NSOperationQueue alloc] init];
198
    [uploadCallbackQueue setSuspended:YES];
199
    uploadCallbackQueue.name = @"gr.grnet.pithos.UploadCallbackQueue";
200
//    uploadCallbackQueue.maxConcurrentOperationCount = 1;
201
    downloadCallbackQueue = [[NSOperationQueue alloc] init];
202
    [downloadCallbackQueue setSuspended:YES];
203
    downloadCallbackQueue.name = @"gr.grnet.pithos.DownloadCallbackQueue";
204
//    downloadCallbackQueue.maxConcurrentOperationCount = 1;
196 205
    
197 206
    [activityProgressIndicator setUsesThreadedAnimation:YES];
198 207
    [activityProgressIndicator setMinValue:0.0];
......
247 256
}
248 257

  
249 258
- (void)resetBrowser {
259
    @synchronized(self) {
260
        if (!browserActive)
261
            return;
262
    }
263

  
250 264
    [refreshTimer invalidate];
251 265
    [refreshTimer release];
252 266
    
253
    [moveNetworkQueue cancelAllOperations];
254
    [copyNetworkQueue cancelAllOperations];
255
    [deleteNetworkQueue cancelAllOperations];
256
    [uploadNetworkQueue cancelAllOperations];
257
    [downloadNetworkQueue cancelAllOperations];
267
    [moveNetworkQueue reset];
268
    [copyNetworkQueue reset];
269
    [deleteNetworkQueue reset];
270
    [uploadNetworkQueue reset];
271
    [downloadNetworkQueue reset];
258 272
    
273
    [moveQueue cancelAllOperations];
274
    [moveQueue setSuspended:YES];
275
    [copyQueue cancelAllOperations];
276
    [copyQueue setSuspended:YES];
277
    [deleteQueue cancelAllOperations];
278
    [deleteQueue setSuspended:YES];
279
    [uploadQueue cancelAllOperations];
280
    [uploadQueue setSuspended:YES];
281
    [downloadQueue cancelAllOperations];
282
    [downloadQueue setSuspended:YES];
283

  
284
    [moveCallbackQueue cancelAllOperations];
285
    [moveCallbackQueue setSuspended:YES];
286
    [copyCallbackQueue cancelAllOperations];
287
    [copyCallbackQueue setSuspended:YES];
288
    [deleteCallbackQueue cancelAllOperations];
289
    [deleteCallbackQueue setSuspended:YES];
290
    [uploadCallbackQueue cancelAllOperations];
291
    [uploadCallbackQueue setSuspended:YES];
292
    [downloadCallbackQueue cancelAllOperations];
293
    [downloadCallbackQueue setSuspended:YES];
294

  
259 295
    rootNode = nil;
260 296
    [browser loadColumnZero];
261 297
    [containersNodeChildren removeAllObjects];
262 298
    [outlineView reloadData];
263
	// Expand the folder outline view
299
    // Expand the folder outline view
264 300
    [outlineView expandItem:nil expandChildren:YES];
265
	[outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
301
    [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
266 302
    
267 303
    activityFacility.delegate = nil;
268 304
    [activityProgressIndicator setDoubleValue:1.0];
269 305
    [activityProgressIndicator stopAnimation:self];
306
    
307
    @synchronized(self) {
308
        browserActive = NO;
309
    }
270 310
}
271 311

  
272 312
- (void)startBrowser {
313
    @synchronized(self) {
314
        if (browserActive)
315
            return;
316
    }
317
    
318
    // In the improbable case of leftover operations
319
    [moveNetworkQueue reset];
320
    [copyNetworkQueue reset];
321
    [deleteNetworkQueue reset];
322
    [uploadNetworkQueue reset];
323
    [downloadNetworkQueue reset];
324
    [moveQueue cancelAllOperations];
325
    [copyQueue cancelAllOperations];
326
    [deleteQueue cancelAllOperations];
327
    [uploadQueue cancelAllOperations];
328
    [downloadQueue cancelAllOperations];
329
    [moveCallbackQueue cancelAllOperations];
330
    [copyCallbackQueue cancelAllOperations];
331
    [deleteCallbackQueue cancelAllOperations];
332
    [uploadCallbackQueue cancelAllOperations];
333
    [downloadCallbackQueue cancelAllOperations];
334

  
335
    [moveNetworkQueue go];
336
    [copyNetworkQueue go];
337
    [deleteNetworkQueue go];
338
    [uploadNetworkQueue go];
339
    [downloadNetworkQueue go];
340
    [moveQueue setSuspended:NO];
341
    [copyQueue setSuspended:NO];
342
    [deleteQueue setSuspended:NO];
343
    [uploadQueue setSuspended:NO];
344
    [downloadQueue setSuspended:NO];
345
    [moveCallbackQueue setSuspended:NO];
346
    [copyCallbackQueue setSuspended:NO];
347
    [deleteCallbackQueue setSuspended:NO];
348
    [uploadCallbackQueue setSuspended:NO];
349
    [downloadCallbackQueue setSuspended:NO];
350

  
273 351
    accountNode.pithos = pithos;
274
    [accountNode refresh];
352
    [accountNode forceRefresh];
275 353
    mySharedNode.pithos = pithos;
276
    [mySharedNode refresh];
354
    [mySharedNode forceRefresh];
277 355
    othersSharedNode.pithos = pithos;
278
    [othersSharedNode refresh];
279
    
280
    [activityFacility reset];
356
    [othersSharedNode forceRefresh];
357
            
358
//    [activityFacility reset];
281 359
    activityFacility.delegate = self;
282
    
283
    refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(forceRefresh:) userInfo:self repeats:YES] retain];
360
            
361
    refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:30 
362
                                                     target:self 
363
                                                   selector:@selector(forceRefresh:) 
364
                                                   userInfo:self 
365
                                                    repeats:YES] retain];
366
    @synchronized(self) {
367
        browserActive = YES;
368
    }
284 369
}
285 370

  
286
- (void)setPithos:(ASIPithos *)aPithos {
287
    if (aPithos && [aPithos isNotEqualTo:pithos]) {
288
        [self resetBrowser];
289
        [pithos release];
290
        pithos = [aPithos retain];
291
        [self startBrowser];
292
    }
371
- (BOOL)operationsPending {
372
    return ([moveNetworkQueue operationCount] || 
373
            [copyNetworkQueue operationCount] || 
374
            [deleteNetworkQueue operationCount] || 
375
            [uploadNetworkQueue operationCount] || 
376
            [downloadNetworkQueue operationCount] || 
377
            [moveQueue operationCount] || 
378
            [copyQueue operationCount] || 
379
            [deleteQueue operationCount] || 
380
            [uploadQueue operationCount] || 
381
            [downloadQueue operationCount] || 
382
            [moveCallbackQueue operationCount] || 
383
            [copyCallbackQueue operationCount] || 
384
            [deleteCallbackQueue operationCount] || 
385
            [uploadCallbackQueue operationCount] || 
386
            [downloadCallbackQueue operationCount]);
293 387
}
294 388

  
295
- (void)windowDidLoad {
296
    [super windowDidLoad];
297
    if (browser && !browserInitialized) {
298
        browserInitialized = YES;
299
        [self initBrowser];
389
- (void)dealloc {
390
    [[NSNotificationCenter defaultCenter] removeObserver:self];
391
    [self resetBrowser];
392
    [moveQueue release];
393
    [copyQueue release];
394
    [deleteQueue release];
395
    [uploadQueue release];
396
    [downloadQueue release];
397
    [moveNetworkQueue release];
398
    [copyNetworkQueue release];
399
    [deleteNetworkQueue release];
400
    [uploadNetworkQueue release];
401
    [downloadNetworkQueue release];
402
    [clipboardParentNode release];
403
    [clipboardNodes release];
404
    [draggedParentNode release];
405
    [draggedNodes release];
406
    [sharedPreviewController release];
407
    [othersSharedNode release];
408
    [mySharedNode release];
409
    [sharedNode release];
410
    [containersNodeChildren release];
411
    [containersNode release];
412
    [accountNode release];
413
    [rootNode release];
414
    [pithos release];
415
    [super dealloc];
416
}
417

  
418
- (void)setPithos:(ASIPithos *)aPithos {
419
    if (aPithos) {
420
        if (![aPithos.authUser isEqualToString:pithos.authUser] || 
421
            ![aPithos.authToken isEqualToString:pithos.authToken] || 
422
            ![aPithos.storageURLPrefix isEqualToString:pithos.storageURLPrefix] ||
423
            ![aPithos.publicURLPrefix isEqualToString:pithos.publicURLPrefix]) {
424
            [self resetBrowser];
425
            [pithos release];
426
            pithos = [aPithos retain];
427
            [self startBrowser];
428
        } else {
429
            [self startBrowser];
430
        }
300 431
    }
301 432
}
302 433

  
434

  
303 435
#pragma mark -
304 436
#pragma mark Observers
305 437

  
......
347 479
        // Create pithos node
348 480
        ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithPithos:pithos 
349 481
                                                                                                            containerName:@"pithos"];
350
        [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
351
        while (![containerRequest isFinished]) {
352
            usleep(1);
353
        }
482
        ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
483
        [networkQueue go];
484
        [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES];
354 485
        if ([containerRequest error]) {
355
            [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
486
            dispatch_async(dispatch_get_main_queue(), ^{
487
                [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
488
            });
356 489
        } else {
357 490
            refreshAccountNode = YES;
358 491
        }
......
361 494
        // Create trash node
362 495
        ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithPithos:pithos 
363 496
                                                                                                            containerName:@"trash"];
364
        [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
365
        while (![containerRequest isFinished]) {
366
            usleep(1);
367
        }
497
        ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
498
        [networkQueue go];
499
        [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES];
368 500
        if ([containerRequest error]) {
369
            [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
501
            dispatch_async(dispatch_get_main_queue(), ^{
502
                [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
503
            });
370 504
        } else {
371 505
            refreshAccountNode = YES;
372 506
        }
......
380 514
    // Expand the folder outline view
381 515
    [outlineView expandItem:nil expandChildren:YES];
382 516
    
383
    if ((rootNode == nil) || (rootNode == containersNode) || (rootNode == sharedNode)) {
517
    if (((rootNode == nil) || (rootNode == containersNode) || (rootNode == sharedNode)) && [containersNodeChildren count]) {
384 518
        rootNode = [containersNodeChildren objectAtIndex:0];
385 519
        [browser loadColumnZero];
386 520
    }
......
489 623
    }
490 624
    if (([node class] == [PithosObjectNode class]) || 
491 625
        (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
492
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
626
        // Operation: Rename (move) an object or subdir/ node
627
        __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
493 628
            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
629
            if (operation.isCancelled) {
630
                [pool drain];
631
                return;
632
            }
494 633
            NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
495 634
            if ([newName hasSuffix:@"/"])
496 635
                destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
......
502 641
                                                error:&error 
503 642
                                          isDirectory:&isDirectory 
504 643
                                       sharingAccount:nil]) {
505
                NSAlert *alert = [[[NSAlert alloc] init] autorelease];
506
                [alert setMessageText:@"Name Taken"];
507
                [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
508
                [alert addButtonWithTitle:@"OK"];
509
                [alert runModal];
644
                dispatch_async(dispatch_get_main_queue(), ^{
645
                    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
646
                    [alert setMessageText:@"Name Taken"];
647
                    [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
648
                    [alert addButtonWithTitle:@"OK"];
649
                    [alert runModal];
650
                });
510 651
                [pool drain];
511 652
                return;
512 653
            } else if (error) {
513 654
                [pool drain];
514 655
                return;
515 656
            }
657
            if (operation.isCancelled) {
658
                [pool drain];
659
                return;
660
            }
516 661
            ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos 
517 662
                                                                                   containerName:node.pithosContainer.name 
518 663
                                                                                      objectName:node.pithosObject.name 
519 664
                                                                        destinationContainerName:node.pithosContainer.name 
520 665
                                                                           destinationObjectName:destinationObjectName 
521 666
                                                                                   checkIfExists:NO];
522
            if (objectRequest) {
667
            if (!operation.isCancelled && objectRequest) {
523 668
                objectRequest.delegate = self;
524 669
                objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
525 670
                objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
......
543 688
                  NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
544 689
                  NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
545 690
                  moveNetworkQueue, @"networkQueue", 
546
                  @"moveQueue", @"queueType", 
691
                  @"move", @"operationType", 
547 692
                  nil]];
548 693
                [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
549 694
            }
550 695
            [pool drain];
551
        });
696
        }];
697
        [moveQueue addOperation:operation];
552 698
    } else if ([node class] == [PithosSubdirNode class]) {
553 699
        if (firstSlashRange.length == 1)
554 700
            return;
555
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
701
        // Operation: Rename (move) a subdir node and its descendants
702
        // The resulting ASIPithosObjectRequests are chained through dependencies
703
        __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
556 704
            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
705
            if (operation.isCancelled) {
706
                [pool drain];
707
                return;
708
            }
557 709
            NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
558 710
            NSError *error = nil;
559 711
            BOOL isDirectory;
......
563 715
                                                error:&error 
564 716
                                          isDirectory:&isDirectory 
565 717
                                       sharingAccount:nil]) {
566
                NSAlert *alert = [[[NSAlert alloc] init] autorelease];
567
                [alert setMessageText:@"Name Taken"];
568
                [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
569
                [alert addButtonWithTitle:@"OK"];
570
                [alert runModal];
718
                dispatch_async(dispatch_get_main_queue(), ^{
719
                    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
720
                    [alert setMessageText:@"Name Taken"];
721
                    [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
722
                    [alert addButtonWithTitle:@"OK"];
723
                    [alert runModal];
724
                });
571 725
                [pool drain];
572 726
                return;
573 727
            } else if (error) {
574 728
                [pool drain];
575 729
                return;
576 730
            }
731
            if (operation.isCancelled) {
732
                [pool drain];
733
                return;
734
            }
577 735
            if (node.pithosObject.subdir)
578 736
                destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
579 737
            NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos 
......
582 740
                                                                    destinationContainerName:node.pithosContainer.name 
583 741
                                                                       destinationObjectName:destinationObjectName 
584 742
                                                                               checkIfExists:NO];
585
            if (objectRequests) {
743
            if (!operation.isCancelled && objectRequests) {
744
                ASIPithosObjectRequest *previousObjectRequest = nil;
586 745
                for (ASIPithosObjectRequest *objectRequest in objectRequests) {
746
                    if (operation.isCancelled) {
747
                        [pool drain];
748
                        return;
749
                    }
587 750
                    objectRequest.delegate = self;
588 751
                    objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
589 752
                    objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
......
607 770
                      NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
608 771
                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
609 772
                      moveNetworkQueue, @"networkQueue", 
610
                      @"moveQueue", @"queueType", 
773
                      @"move", @"operationType", 
611 774
                      nil]];
775
                    if (previousObjectRequest)
776
                        [objectRequest addDependency:previousObjectRequest];
777
                    previousObjectRequest = objectRequest;
612 778
                    [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
613 779
                }
614 780
            }
615 781
            [pool drain];
616
        });
782
        }];
783
        [moveQueue addOperation:operation];
617 784
    }
618 785
}
619 786

  
......
664 831
            [alert addButtonWithTitle:@"Cancel"];
665 832
            NSInteger choice = [alert runModal];
666 833
            if (choice == NSAlertFirstButtonReturn) {
667
                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
834
                // Operation: Download a subdir node and its descendants
835
                // The resulting ASIPithosObjectRequests are chained through dependencies
836
                __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
668 837
                    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
838
                    if (operation.isCancelled) {
839
                        [pool drain];
840
                        return;
841
                    }
669 842
                    NSArray *objectRequests = [PithosUtilities objectDataRequestsForSubdirWithPithos:pithos 
670 843
                                                                                       containerName:node.pithosContainer.name 
671 844
                                                                                          objectName:node.pithosObject.name 
672 845
                                                                                         toDirectory:[dropDestination path] 
673 846
                                                                                       checkIfExists:YES 
674 847
                                                                                      sharingAccount:node.sharingAccount];
675
                    if (objectRequests) {
848
                    if (!operation.isCancelled && objectRequests) {
849
                        ASIPithosObjectRequest *previousObjectRequest = nil;
676 850
                        for (__block ASIPithosObjectRequest *objectRequest in objectRequests) {
851
                            if (operation.isCancelled) {
852
                                [pool drain];
853
                                return;
854
                            }
677 855
                            [names addObject:[objectRequest.userInfo objectForKey:@"fileName"]];
678 856
                            objectRequest.delegate = self;
679 857
                            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
......
697 875
                              NSStringFromSelector(@selector(downloadObjectFinished:)), @"didFinishSelector", 
698 876
                              NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
699 877
                              downloadNetworkQueue, @"networkQueue", 
700
                              @"downloadQueue", @"queueType", 
878
                              @"download", @"operationType", 
701 879
                              nil]];
702 880
                            [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
703 881
                                [activityFacility updateActivity:activity 
......
705 883
                                                      totalBytes:activity.totalBytes 
706 884
                                                    currentBytes:(activity.currentBytes + size)];
707 885
                            }];
886
                            if (previousObjectRequest)
887
                                [objectRequest addDependency:previousObjectRequest];
888
                            previousObjectRequest = objectRequest;
708 889
                            [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
709 890
                        }
710 891
                    }
711 892
                    [pool drain];
712
                });
893
                }];
894
                [downloadQueue addOperation:operation];
713 895
            }
714 896
        } else {
715
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
897
            // Operation: Download an object node
898
            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
716 899
                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
900
                if (operation.isCancelled) {
901
                    [pool drain];
902
                    return;
903
                }
717 904
                __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectDataRequestWithPithos:pithos 
718 905
                                                                                               containerName:node.pithosContainer.name 
719 906
                                                                                                  objectName:node.pithosObject.name 
720 907
                                                                                                 toDirectory:[dropDestination path] 
721 908
                                                                                               checkIfExists:YES 
722 909
                                                                                              sharingAccount:node.sharingAccount];
723
                if (objectRequest) {
910
                if (!operation.isCancelled && objectRequest) {
724 911
                    [names addObject:[objectRequest.userInfo objectForKey:@"fileName"]];
725 912
                    objectRequest.delegate = self;
726 913
                    objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
......
744 931
                      NSStringFromSelector(@selector(downloadObjectFinished:)), @"didFinishSelector", 
745 932
                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
746 933
                      downloadNetworkQueue, @"networkQueue", 
747
                      @"downloadQueue", @"queueType", 
934
                      @"download", @"operationType", 
748 935
                      nil]];
749 936
                    [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
750 937
                        [activityFacility updateActivity:activity 
......
755 942
                    [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
756 943
                    [pool drain];
757 944
                }
758
            });
945
            }];
946
            [downloadQueue addOperation:operation];
759 947
        }
760 948
    }
761 949
    return names;
......
885 1073
    if ((destinationNode.pithosContainer.blockHash == nil) || (destinationNode.pithosContainer.blockSize == 0)) {
886 1074
        ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest containerMetadataRequestWithPithos:pithos 
887 1075
                                                                                                      containerName:containerName];
888
        [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
889
        while (![containerRequest isFinished]) {
890
            usleep(1);
891
        }
1076
        ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
1077
        [networkQueue go];
1078
        [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES];
892 1079
        if ([containerRequest error]) {
893 1080
            [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
894 1081
            return NO;
......
908 1095
            if (!isDirectory) {
909 1096
                // Upload file
910 1097
                NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
911
                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1098
                // Operation: Upload a local file
1099
                __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
912 1100
                    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1101
                    if (operation.isCancelled) {
1102
                        [pool drain];
1103
                        return;
1104
                    }
913 1105
                    NSError *error = nil;
914 1106
                    NSString *contentType = [PithosUtilities contentTypeOfFile:filePath error:&error];
915 1107
                    if (contentType == nil)
......
917 1109
                    if (error)
918 1110
                        NSLog(@"contentType detection error: %@", error);
919 1111
                    NSArray *hashes = nil;
1112
                    if (operation.isCancelled) {
1113
                        [pool drain];
1114
                        return;
1115
                    }
920 1116
                    ASIPithosObjectRequest *objectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos 
921 1117
                                                                                                containerName:containerName 
922 1118
                                                                                                   objectName:objectName 
......
927 1123
                                                                                                checkIfExists:YES 
928 1124
                                                                                                       hashes:&hashes 
929 1125
                                                                                               sharingAccount:destinationNode.sharingAccount];
930
                    if (objectRequest) {
1126
                    if (!operation.isCancelled && objectRequest) {
931 1127
                        objectRequest.delegate = self;
932 1128
                        objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
933 1129
                        objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
......
957 1153
                          NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", 
958 1154
                          NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
959 1155
                          uploadNetworkQueue, @"networkQueue", 
960
                          @"uploadQueue", @"queueType", 
1156
                          @"upload", @"operationType", 
961 1157
                          nil]];
962 1158
                        if (destinationNode.sharingAccount)
963 1159
                            [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
964 1160
                        [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
965 1161
                    }
966 1162
                    [pool drain];
967
                });
1163
                }];
1164
                [uploadQueue addOperation:operation];
968 1165
            } else {
969 1166
                // Upload directory, confirm first
970 1167
                NSAlert *alert = [[[NSAlert alloc] init] autorelease];
......
975 1172
                NSInteger choice = [alert runModal];
976 1173
                if (choice == NSAlertFirstButtonReturn) {
977 1174
                    NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
978
                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1175
                    // Operation: Upload a local directory and its descendants
1176
                    // The resulting ASIPithosObjectRequests are chained through dependencies
1177
                    __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
979 1178
                        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1179
                        if (operation.isCancelled) {
1180
                            [pool drain];
1181
                            return;
1182
                        }
980 1183
                        NSMutableArray *objectNames = nil;
981 1184
                        NSMutableArray *contentTypes = nil;
982 1185
                        NSMutableArray *filePaths = nil;
......
995 1198
                                                                                        hashesArrays:&hashesArrays 
996 1199
                                                                             directoryObjectRequests:&directoryObjectRequests 
997 1200
                                                                                      sharingAccount:destinationNode.sharingAccount];
1201
                        if (operation.isCancelled) {
1202
                            [pool drain];
1203
                            return;
1204
                        }
1205
                        ASIPithosObjectRequest *previousObjectRequest = nil;
998 1206
                        for (ASIPithosObjectRequest *objectRequest in directoryObjectRequests) {
1207
                            if (operation.isCancelled) {
1208
                                [pool drain];
1209
                                return;
1210
                            }
999 1211
                            objectRequest.delegate = self;
1000 1212
                            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1001 1213
                            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
......
1014 1226
                              NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
1015 1227
                              NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
1016 1228
                              uploadNetworkQueue, @"networkQueue", 
1017
                              @"uploadQueue", @"queueType", 
1229
                              @"upload", @"queue", 
1018 1230
                              nil]];
1231
                            if (previousObjectRequest)
1232
                                [objectRequest addDependency:previousObjectRequest];
1233
                            previousObjectRequest = objectRequest;
1019 1234
                            [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
1020 1235
                        }
1021
                        if (objectRequests) {
1236
                        if (!operation.isCancelled && objectRequests) {
1022 1237
                            for (NSUInteger i = 0 ; i < [objectRequests count] ; i++) {
1238
                                if (operation.isCancelled) {
1239
                                    [pool drain];
1240
                                    return;
1241
                                }
1023 1242
                                ASIPithosObjectRequest *objectRequest = [objectRequests objectAtIndex:i];
1024 1243
                                objectRequest.delegate = self;
1025 1244
                                objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
......
1049 1268
                                  NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", 
1050 1269
                                  NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
1051 1270
                                  uploadNetworkQueue, @"networkQueue", 
1052
                                  @"uploadQueue", @"queueType", 
1271
                                  @"upload", @"queue", 
1053 1272
                                  nil]];
1054 1273
                                if (destinationNode.sharingAccount)
1055 1274
                                    [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
1275
                                if (previousObjectRequest)
1276
                                    [objectRequest addDependency:previousObjectRequest];
1277
                                previousObjectRequest = objectRequest;
1056 1278
                                [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
1057 1279
                            }
1058 1280
                        }
1059 1281
                        [pool drain];
1060
                    });
1282
                    }];
1283
                    [uploadQueue addOperation:operation];
1061 1284
                }
1062 1285
            }
1063 1286
        }
......
1079 1302
    for (PithosNode *node in nodes) {
1080 1303
        if (([node class] == [PithosObjectNode class]) || 
1081 1304
            (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
1082
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1305
            // Operation: Move an object or subdir/ node
1306
            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
1083 1307
                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1308
                if (operation.isCancelled) {
1309
                    [pool drain];
1310
                    return;
1311
                }
1084 1312
                NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
1085 1313
                if ([node.pithosObject.name hasSuffix:@"/"])
1086 1314
                    destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
......
1090 1318
                                                                            destinationContainerName:containerName 
1091 1319
                                                                               destinationObjectName:destinationObjectName 
1092 1320
                                                                                       checkIfExists:YES];
1093
                if (objectRequest) {
1321
                if (!operation.isCancelled && objectRequest) {
1094 1322
                    objectRequest.delegate = self;
1095 1323
                    objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1096 1324
                    objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
......
1113 1341
                      NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
1114 1342
                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
1115 1343
                      moveNetworkQueue, @"networkQueue", 
1116
                      @"moveQueue", @"queueType", 
1344
                      @"move", @"operationType", 
1117 1345
                      nil]];
1118 1346
                    [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
1119 1347
                }
1120 1348
                [pool drain];
1121
            });
1349
            }];
1350
            [moveQueue addOperation:operation];
1122 1351
        } else if ([node class] == [PithosSubdirNode class]) {
1123
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1352
            // Operation: Move a subdir node and its descendants
1353
            // The resulting ASIPithosObjectRequests are chained through dependencies
1354
            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
1124 1355
                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1356
                if (operation.isCancelled) {
1357
                    [pool drain];
1358
                    return;
1359
                }
1125 1360
                NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
1126 1361
                if (node.pithosObject.subdir)
1127 1362
                    destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
......
1131 1366
                                                                        destinationContainerName:containerName 
1132 1367
                                                                           destinationObjectName:destinationObjectName 
1133 1368
                                                                                   checkIfExists:YES];
1134
                if (objectRequests) {
1369
                if (!operation.isCancelled && objectRequests) {
1370
                    ASIPithosObjectRequest *previousObjectRequest = nil;
1135 1371
                    for (ASIPithosObjectRequest *objectRequest in objectRequests) {
1372
                        if (operation.isCancelled) {
1373
                            [pool drain];
1374
                            return;
1375
                        }
1136 1376
                        objectRequest.delegate = self;
1137 1377
                        objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1138 1378
                        objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
......
1156 1396
                          NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
1157 1397
                          NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
1158 1398
                          moveNetworkQueue, @"networkQueue", 
1159
                          @"moveQueue", @"queueType", 
1399
                          @"move", @"operationType", 
1160 1400
                          nil]];
1401
                        if (previousObjectRequest)
1402
                            [objectRequest addDependency:previousObjectRequest];
1403
                        previousObjectRequest = objectRequest;
1161 1404
                        [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
1162 1405
                    }
1163 1406
                }
1164 1407
                [pool drain];
1165
            });
1408
            }];
1409
            [moveQueue addOperation:operation];
1166 1410
        }
1167 1411
    }
1168 1412
    return YES;
......
1182 1426
    for (PithosNode *node in nodes) {
1183 1427
        if (([node class] == [PithosObjectNode class]) || 
1184 1428
            (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
1185
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1429
            // Operation: Copy an object or subdir/ node
1430
            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
1186 1431
                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1432
                if (operation.isCancelled) {
1433
                    [pool drain];
1434
                    return;
1435
                }
1187 1436
                NSString *destinationObjectName;
1188 1437
                if (![destinationNode isEqualTo:node.parent]) {
1189 1438
                    destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
......
1194 1443
                                                                       containerName:containerName 
1195 1444
                                                                          objectName:node.pithosObject.name];
1196 1445
                }
1446
                if (operation.isCancelled) {
1447
                    [pool drain];
1448
                    return;
1449
                }
1197 1450
                ASIPithosObjectRequest *objectRequest = [PithosUtilities copyObjectRequestWithPithos:pithos 
1198 1451
                                                                                       containerName:node.pithosContainer.name 
1199 1452
                                                                                          objectName:node.pithosObject.name 
......
1201 1454
                                                                               destinationObjectName:destinationObjectName 
1202 1455
                                                                                       checkIfExists:YES 
1203 1456
                                                                                      sharingAccount:node.sharingAccount];
1204
                if (objectRequest) {
1457
                if (!operation.isCancelled && objectRequest) {
1205 1458
                    objectRequest.delegate = self;
1206 1459
                    objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1207 1460
                    objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
......
1224 1477
                      NSStringFromSelector(@selector(copyFinished:)), @"didFinishSelector", 
1225 1478
                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
1226 1479
                      copyNetworkQueue, @"networkQueue", 
1227
                      @"copyQueue", @"queueType", 
1480
                      @"copy", @"operationType", 
1228 1481
                      nil]];
1229 1482
                    [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
1230 1483
                }
1231 1484
                [pool drain];
1232
            });
1485
            }];
1486
            [copyQueue addOperation:operation];
1233 1487
        } else if ([node class] == [PithosSubdirNode class]) {
1234
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1488
            // Operation: Copy a subdir node and its descendants
1489
            // The resulting ASIPithosObjectRequests are chained through dependencies
1490
            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
1235 1491
                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1492
                if (operation.isCancelled) {
1493
                    [pool drain];
1494
                    return;
1495
                }
1236 1496
                NSString *destinationObjectName;
1237 1497
                if (![destinationNode isEqualTo:node.parent]) {
1238 1498
                    destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
......
1243 1503
                                                                       containerName:containerName 
1244 1504
                                                                          subdirName:node.pithosObject.name];
1245 1505
                }
1506
                if (operation.isCancelled) {
1507
                    [pool drain];
1508
                    return;
1509
                }
1246 1510
                NSArray *objectRequests = [PithosUtilities copyObjectRequestsForSubdirWithPithos:pithos 
1247 1511
                                                                                   containerName:node.pithosContainer.name 
1248 1512
                                                                                      objectName:node.pithosObject.name 
......
1250 1514
                                                                           destinationObjectName:destinationObjectName 
1251 1515
                                                                                   checkIfExists:YES 
1252 1516
                                                                                  sharingAccount:node.sharingAccount];
1253
                if (objectRequests) {
1517
                if (!operation.isCancelled && objectRequests) {
1518
                    ASIPithosObjectRequest *previousObjectRequest = nil;
1254 1519
                    for (ASIPithosObjectRequest *objectRequest in objectRequests) {
1520
                        if (operation.isCancelled) {
1521
                            [pool drain];
1522
                            return;
1523
                        }
1255 1524
                        objectRequest.delegate = self;
1256 1525
                        objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
1257 1526
                        objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
......
1274 1543
                          NSStringFromSelector(@selector(copyFinished:)), @"didFinishSelector", 
1275 1544
                          NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
1276 1545
                          copyNetworkQueue, @"networkQueue", 
1277
                          @"copyQueue", @"queueType", 
1546
                          @"copy", @"operationType", 
1278 1547
                          nil]];
1548
                        if (previousObjectRequest)
1549
                            [objectRequest addDependency:previousObjectRequest];
1550
                        previousObjectRequest = objectRequest;
1279 1551
                        [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
1280 1552
                    }
1281 1553
                }
1282 1554
                [pool drain];
1283
            });
1555
            }];
1556
            [copyQueue addOperation:operation];
1284 1557
        }
1285 1558
    }
1286 1559
    return YES;
......
1290 1563
#pragma mark ASIHTTPRequestDelegate
1291 1564

  
1292 1565
- (void)performRequestFinishedDelegateInBackground:(ASIPithosRequest *)request {
1293
    dispatch_queue_t queue;
1294
    if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"moveQueue"])
1295
        queue = moveQueue;
1296
    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"copyQueue"])
1297
        queue = copyQueue;
1298
    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"deleteQueue"])
1299
        queue = deleteQueue;
1300
    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"uploadQueue"])
1301
        queue = uploadQueue;
1302
    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"downloadQueue"])
1303
        queue = downloadQueue;
1566
    NSOperationQueue *callbackQueue;
1567
    NSString *operationType = [request.userInfo objectForKey:@"operationType"];
1568
    if ([operationType isEqualToString:@"move"])
1569
        callbackQueue = moveCallbackQueue;
1570
    else if ([operationType isEqualToString:@"copy"])
1571
        callbackQueue = copyCallbackQueue;
1572
    else if ([operationType isEqualToString:@"delete"])
1573
        callbackQueue = deleteCallbackQueue;
1574
    else if ([operationType isEqualToString:@"upload"])
1575
        callbackQueue = uploadCallbackQueue;
1576
    else if ([operationType isEqualToString:@"download"])
1577
        callbackQueue = downloadCallbackQueue;
1304 1578
    else
1305
        return;
1306
    dispatch_async(queue, ^{
1307
        [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) withObject:request];
1308
    });
1579
        dispatch_async(dispatch_get_main_queue(), ^{
1580
            [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
1581
                              withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
1582
        });
1583
    // Add an operation to the callbackQueue with a completionBlock for the case of cancellation
1584
    NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self 
1585
                                                                             selector:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) 
1586
                                                                               object:request] autorelease];
1587
    operation.completionBlock = ^{
1588
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1589
        if ([[request.userInfo objectForKey:@"operation"] isCancelled]) {
1590
            dispatch_async(dispatch_get_main_queue(), ^{
1591
                [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
1592
                                  withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
1593
            });
1594
        }
1595
        [pool drain];
1596
    };
1597
    [(NSMutableDictionary *)request.userInfo setObject:operation forKey:@"operation"];
1598
    [callbackQueue addOperation:operation];
1309 1599
}
1310 1600

  
1311 1601
- (void)performRequestFailedDelegateInBackground:(ASIPithosRequest *)request {
1312
    dispatch_queue_t queue;
1313
    if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"moveQueue"])
1314
        queue = moveQueue;
1315
    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"copyQueue"])
1316
        queue = copyQueue;
1317
    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"deleteQueue"])
1318
        queue = deleteQueue;
1319
    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"uploadQueue"])
1320
        queue = uploadQueue;
1321
    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"downloadQueue"])
1322
        queue = downloadQueue;
1323
    else
1324
        return;
1325
    dispatch_async(queue, ^{
1326
        [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) withObject:request];
1327
    });
1602
    if (request.isCancelled) {
1603
        // Request has been cancelled 
1604
        // The callbackQueue might be suspended so we call directly the callback method, since it does minimal work anyway
1605
        [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) 
1606
                   withObject:request];
1607
    } else {
1608
        NSOperationQueue *callbackQueue;
1609
        NSString *operationType = [request.userInfo objectForKey:@"operationType"];
1610
        if ([operationType isEqualToString:@"move"])
1611
            callbackQueue = moveCallbackQueue;
1612
        else if ([operationType isEqualToString:@"copy"])
1613
            callbackQueue = copyCallbackQueue;
1614
        else if ([operationType isEqualToString:@"delete"])
1615
            callbackQueue = deleteCallbackQueue;
1616
        else if ([operationType isEqualToString:@"upload"])
1617
            callbackQueue = uploadCallbackQueue;
1618
        else if ([operationType isEqualToString:@"download"])
1619
            callbackQueue = downloadCallbackQueue;
1620
        else
1621
            dispatch_async(dispatch_get_main_queue(), ^{
1622
                [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
1623
                                  withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
1624
            });
1625
        // Add an operation to the callbackQueue with a completionBlock for the case of cancellation
1626
        NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self 
1627
                                                                                 selector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) 
1628
                                                                                   object:request] autorelease];
1629
        operation.completionBlock = ^{
1630
            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1631
            if ([[request.userInfo objectForKey:@"operation"] isCancelled]) {
1632
                dispatch_async(dispatch_get_main_queue(), ^{
1633
                    [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
1634
                                      withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
1635
                });
1636
            }
1637
            [pool drain];
1638
        };
1639
        [(NSMutableDictionary *)request.userInfo setObject:operation forKey:@"operation"];
1640
        [callbackQueue addOperation:operation];
1641
    }
1328 1642
}
1329 1643

  
1330 1644
- (void)requestFailed:(ASIPithosRequest *)request {
1331 1645
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1646
    NSOperation *operation = [request.userInfo objectForKey:@"operation"];
1332 1647
    NSLog(@"Request failed: %@", request.url);
1333
    if ([request isCancelled]) {
1648
    if (operation.isCancelled) {
1649
        [pool drain];
1650
        return;        
1651
    }
1652
    if (request.isCancelled) {
1334 1653
        dispatch_async(dispatch_get_main_queue(), ^{
1335 1654
            [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
1336 1655
                              withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
......
1359 1678

  
1360 1679
- (void)downloadObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1361 1680
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1681
    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
1362 1682
    NSLog(@"Download finished: %@", objectRequest.url);
1363
    if (objectRequest.responseStatusCode == 200) {
1683
    if (operation.isCancelled) {
1684
        [self requestFailed:objectRequest];
1685
    } else if (objectRequest.responseStatusCode == 200) {
1364 1686
        NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"];
1365 1687
        PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
1366 1688
        NSUInteger totalBytes = activity.totalBytes;
......
1400 1722

  
1401 1723
- (void)uploadDirectoryObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1402 1724
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1725
    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
1403 1726
    NSLog(@"Upload directory object finished: %@", objectRequest.url);
1404
    if (objectRequest.responseStatusCode == 201) {
1727
    if (operation.isCancelled) {
1728
        [self requestFailed:objectRequest];
1729
    } else if (objectRequest.responseStatusCode == 201) {
1405 1730
        NSLog(@"Directory object created: %@", objectRequest.url);
1406 1731
        dispatch_async(dispatch_get_main_queue(), ^{
1407 1732
            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
......
1426 1751

  
1427 1752
- (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest {
1428 1753
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1754
    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
1429 1755
    NSLog(@"Upload using hashmap finished: %@", objectRequest.url);
1430 1756
    NSString *fileName = [objectRequest.userInfo objectForKey:@"fileName"];
1431 1757
    PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
1432 1758
    NSUInteger totalBytes = activity.totalBytes;
1433 1759
    NSUInteger currentBytes = activity.currentBytes;
1434
    if (objectRequest.responseStatusCode == 201) {
1760
    if (operation.isCancelled) {
1761
        [self requestFailed:objectRequest];
1762
    } else if (objectRequest.responseStatusCode == 201) {
1435 1763
        NSLog(@"Object created: %@", objectRequest.url);
1436 1764
        dispatch_async(dispatch_get_main_queue(), ^{
1437 1765
            [activityFacility endActivity:activity 
......
1510 1838

  
1511 1839
- (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest {
1512 1840
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1841
    NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"];
1513 1842
    NSLog(@"Upload of missing block finished: %@", containerRequest.url);
1514 1843
    NSUInteger blockSize = [[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue];
1515 1844
    NSString *fileName = [containerRequest.userInfo objectForKey:@"fileName"];
1516 1845
    PithosActivity *activity = [containerRequest.userInfo objectForKey:@"activity"];
1517
    if (containerRequest.responseStatusCode == 202) {
1846
    if (operation.isCancelled) {
1847
        [self requestFailed:containerRequest];
1848
    } else if (containerRequest.responseStatusCode == 202) {
1518 1849
        NSIndexSet *missingBlocks = [containerRequest.userInfo objectForKey:@"missingBlocks"];
1519 1850
        NSUInteger missingBlockIndex = [[containerRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue];
1520 1851
        missingBlockIndex = [missingBlocks indexGreaterThanIndex:missingBlockIndex];
......
1569 1900

  
1570 1901
- (void)moveFinished:(ASIPithosObjectRequest *)objectRequest {
1571 1902
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1903
    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
1572 1904
    NSLog(@"Move object finished: %@", objectRequest.url);
1573
    if (objectRequest.responseStatusCode == 201) {
1905
    if (operation.isCancelled) {
1906
        [self requestFailed:objectRequest];
1907
    } else if (objectRequest.responseStatusCode == 201) {
1574 1908
        dispatch_async(dispatch_get_main_queue(), ^{
1575 1909
            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
1576 1910
                              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
......
1594 1928

  
1595 1929
- (void)copyFinished:(ASIPithosObjectRequest *)objectRequest {
1596 1930
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1931
    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
1597 1932
    NSLog(@"Copy object finished: %@", objectRequest.url);
1598
    if (objectRequest.responseStatusCode == 201) {
1933
    if (operation.isCancelled) {
1934
        [self requestFailed:objectRequest];
1935
    } else if (objectRequest.responseStatusCode == 201) {
1599 1936
        dispatch_async(dispatch_get_main_queue(), ^{
1600 1937
            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
1601 1938
                              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
......
1619 1956

  
1620 1957
- (void)deleteObjectFinished:(ASIPithosObjectRequest *)objectRequest {
1621 1958
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1959
    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
1622 1960
    NSLog(@"Delete object finished: %@", objectRequest.url);
1623
    if (objectRequest.responseStatusCode == 204) {
1961
    if (operation.isCancelled) {
1962
        [self requestFailed:objectRequest];
1963
    } else if (objectRequest.responseStatusCode == 204) {
1624 1964
        dispatch_async(dispatch_get_main_queue(), ^{
1625 1965
            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
1626 1966
                              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
......
1945 2285
- (void)menuNewFolder:(NSMenuItem *)sender {
1946 2286
    PithosNode *node = (PithosNode *)[sender representedObject];
1947 2287
    if ([node class] == [PithosContainerNode class]) {
1948
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2288
        // Operation: Create (upload) a new root application/directory object
2289
        __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
1949 2290
            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2291
            if (operation.isCancelled) {
2292
                [pool drain];
2293
                return;
2294
            }
1950 2295
            NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos 
1951 2296
                                                                  containerName:node.pithosContainer.name 
1952 2297
                                                                     subdirName:@"untitled folder"];
1953 2298
            NSString *fileName = [safeObjectName lastPathComponent];
2299
            if (operation.isCancelled) {
2300
                [pool drain];
2301
                return;
2302
            }
1954 2303
            ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
1955 2304
                                                                                               containerName:node.pithosContainer.name 
1956 2305
                                                                                                  objectName:safeObjectName 
......
1982 2331
                                      NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
1983 2332
                                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
1984 2333
                                      uploadNetworkQueue, @"networkQueue", 
1985
                                      @"uploadQueue", @"queueType", 
2334
                                      @"upload", @"operationType", 
1986 2335
                                      nil];
1987 2336
            [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
1988 2337
            [pool drain];
1989
        });
2338
        }];
2339
        [uploadQueue addOperation:operation];
1990 2340
    } else if (([node class] == [PithosSubdirNode class]) && 
1991 2341
               (node.pithosObject.subdir || ![node.pithosObject.name hasSuffix:@"/"])) {
1992
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2342
        // Operation: Create (upload) a new aplication/directory object
2343
        __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
1993 2344
            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2345
            if (operation.isCancelled) {
2346
                [pool drain];
2347
                return;
2348
            }
1994 2349
            NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos 
1995 2350
                                                                  containerName:node.pithosContainer.name 
1996 2351
                                                                     subdirName:[node.pithosObject.name stringByAppendingPathComponent:@"untitled folder"]];
1997 2352
            NSString *fileName = [safeObjectName lastPathComponent];
2353
            if (operation.isCancelled) {
2354
                [pool drain];
2355
                return;
2356
            }
1998 2357
            ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
1999 2358
                                                                                               containerName:node.pithosContainer.name 
2000 2359
                                                                                                  objectName:safeObjectName 
......
2026 2385
                                      NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
2027 2386
                                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
2028 2387
                                      uploadNetworkQueue, @"networkQueue", 
2029
                                      @"uploadQueue", @"queueType", 
2388
                                      @"upload", @"operationType", 
2030 2389
                                      nil];
2031 2390
            [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
2032 2391
            [pool drain];
2033
        });
2392
        }];
2393
        [uploadQueue addOperation:operation];
2034 2394
    }
2035 2395
}
2036 2396

  
......
2044 2404
    for (PithosNode *node in ((NSArray *)[sender representedObject])) {
2045 2405
        if (([node class] == [PithosObjectNode class]) || 
2046 2406
            (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
2047
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2407
            // Operation: Delete an object or subdir/ node
2408
            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
2048 2409
                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2410
                if (operation.isCancelled) {
2411
                    [pool drain];
2412
                    return;
2413
                }
2049 2414
                NSString *fileName = [node.pithosObject.name lastPathComponent];
2050 2415
                if ([node.pithosObject.name hasSuffix:@"/"])
2051 2416
                    fileName = [fileName stringByAppendingString:@"/"];
2052 2417
                ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos 
2053 2418
                                                                                                containerName:node.pithosContainer.name 
2054 2419
                                                                                                   objectName:node.pithosObject.name];
2420
                if (operation.isCancelled) {
2421
                    [pool drain];
2422
                    return;
2423
                }
2055 2424
                objectRequest.delegate = self;
2056 2425
                objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
2057 2426
                objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
......
2070 2439
                                          NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", 
2071 2440
                                          NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
2072 2441
                                          deleteNetworkQueue, @"networkQueue", 
2073
                                          @"deleteQueue", @"queueType", 
2442
                                          @"delete", @"operationType", 
2074 2443
                                          nil];
2075 2444
                [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
2076 2445
                [pool drain];
2077
            });
2446
            }];
2447
            [deleteQueue addOperation:operation];
2078 2448
        } else if ([node class] == [PithosSubdirNode class]) {
2079
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2449
            // Operation: Delete a subdir node and its descendants
2450
            // The resulting ASIPithosObjectRequests are chained through dependencies
2451
            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
2080 2452
                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2453
                if (operation.isCancelled) {
2454
                    [pool drain];
2455
                    return;
2456
                }
2081 2457
                NSArray *objectRequests = [PithosUtilities deleteObjectRequestsForSubdirWithPithos:pithos 
2082 2458
                                                                                     containerName:node.pithosContainer.name 
2083 2459
                                                                                        objectName:node.pithosObject.name];
2084
                if (objectRequests) {
2460
                if (!operation.isCancelled && objectRequests) {
2461
                    ASIPithosObjectRequest *previousObjectRequest = nil;
2085 2462
                    for (ASIPithosObjectRequest *objectRequest in objectRequests) {
2463
                        if (operation.isCancelled) {
2464
                            [pool drain];
2465
                            return;
2466
                        }
2086 2467
                        objectRequest.delegate = self;
2087 2468
                        objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
2088 2469
                        objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
......
2101 2482
                          NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", 
2102 2483
                          NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
2103 2484
                          deleteNetworkQueue, @"networkQueue", 
2104
                          @"deleteQueue", @"queueType", 
2485
                          @"delete", @"operationType", 
2105 2486
                          nil]];
2487
                        if (previousObjectRequest)
2488
                            [objectRequest addDependency:previousObjectRequest];
2489
                        previousObjectRequest = objectRequest;
2106 2490
                        [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
2107 2491
                    }
2108 2492
                }
2109 2493
                [pool drain];
2110
            });
2494
            }];
2495
            [deleteQueue addOperation:operation];
2111 2496
        }
2112 2497
    }
2113 2498
}
......
2118 2503
            (([node class] == [PithosSubdirNode class]) && 
2119 2504
             !node.pithosObject.subdir &&
2120 2505
             [node.pithosObject.name hasSuffix:@"/"])) {
2121
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2506
            // Operation: Move to trash an object or subdir/ node
2507
            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
2122 2508
                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2509
                if (operation.isCancelled) {
2510
                    [pool drain];
2511
                    return;
2512
                }
2123 2513
                NSString *safeObjectName = [PithosUtilities safeObjectNameForPithos:pithos 
2124 2514
                                                                      containerName:@"trash" 
2125 2515
                                                                         objectName:node.pithosObject.name];
2126
                if (safeObjectName) {
2516
                if (!operation.isCancelled && safeObjectName) {
2127 2517
                    ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos 
2128 2518
                                                                                           containerName:node.pithosContainer.name 
2129 2519
                                                                                              objectName:node.pithosObject.name 
2130 2520
                                                                                destinationContainerName:@"trash" 
2131 2521
                                                                                   destinationObjectName:safeObjectName 
2132 2522
                                                                                           checkIfExists:NO];
2133
                    if (objectRequest) {
2523
                    if (!operation.isCancelled && objectRequest) {
2134 2524
                        objectRequest.delegate = self;
2135 2525
                        objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
2136 2526
                        objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
......
2153 2543
                          NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
2154 2544
                          NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
2155 2545
                          moveNetworkQueue, @"networkQueue", 
2156
                          @"moveQueue", @"queueType", 
2546
                          @"move", @"operationType", 
2157 2547
                          nil]];
2158 2548
                        [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
2159 2549
                    }
2160 2550
                }
2161 2551
                [pool drain];
2162
            });
2552
            }];
2553
            [moveQueue addOperation:operation];
2163 2554
        } else if ([node class] == [PithosSubdirNode class]) {
2164
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2555
            // Operation: Move to trash a subdir node and its descendants
2556
            // The resulting ASIPithosObjectRequests are chained through dependencies
2557
            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
2165 2558
                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2559
                if (operation.isCancelled) {
2560
                    [pool drain];
2561
                    return;
2562
                }
2166 2563
                NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos 
2167 2564
                                                                      containerName:@"trash" 
2168 2565
                                                                         subdirName:node.pithosObject.name];
2169
                if (safeObjectName) {
2566
                if (!operation.isCancelled && safeObjectName) {
2170 2567
                    NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos 
2171 2568
                                                                                       containerName:node.pithosContainer.name 
2172 2569
                                                                                          objectName:node.pithosObject.name 
2173 2570
                                                                            destinationContainerName:@"trash" 
2174 2571
                                                                               destinationObjectName:safeObjectName 
2175 2572
                                                                                       checkIfExists:NO];
2176
                    if (objectRequests) {
2573
                    if (!operation.isCancelled && objectRequests) {
2574
                        ASIPithosObjectRequest *previousObjectRequest = nil;
2177 2575
                        for (ASIPithosObjectRequest *objectRequest in objectRequests) {
2576
                            if (operation.isCancelled) {
2577
                                [pool drain];
2578
                                return;
2579
                            }
2178 2580
                            objectRequest.delegate = self;
2179 2581
                            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
2180 2582
                            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
......
2197 2599
                              NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
2198 2600
                              NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
2199 2601
                              moveNetworkQueue, @"networkQueue", 
2200
                              @"moveQueue", @"queueType", 
2602
                              @"move", @"operationType", 
2201 2603
                              nil]];
2604
                            if (previousObjectRequest)
2605
                                [objectRequest addDependency:previousObjectRequest];
2606
                            previousObjectRequest = objectRequest;
2202 2607
                            [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
2203 2608
                        }
2204 2609
                    }
2205 2610
                }
2206 2611
                [pool drain];
2207
            });
2612
            }];
2613
            [moveQueue addOperation:operation];
2208 2614
        }
2209 2615
    }
2210 2616
}

Also available in: Unified diff