commit d8426ffb6e7d080257461963a7fef18d48203e9c
Author: Miltiadis Vasilakis <mvasilak@gmail.com>
Date:   Wed Feb 29 21:51:58 2012 +0200

    Support multiple accounts. Bug fixes. Improve concurrency.

diff --git a/pithos-macos/PithosBrowserController.m b/pithos-macos/PithosBrowserController.m
index b976f87..0bcb45e 100644
--- a/pithos-macos/PithosBrowserController.m
+++ b/pithos-macos/PithosBrowserController.m
@@ -120,39 +120,12 @@
     return [super initWithWindowNibName:@"PithosBrowserController"];
 }
 
-- (void)dealloc {
-    [[NSNotificationCenter defaultCenter] removeObserver:self];
-    dispatch_release(moveQueue);
-    dispatch_release(copyQueue);
-    dispatch_release(deleteQueue);
-    dispatch_release(uploadQueue);
-    dispatch_release(downloadQueue);
-    [moveNetworkQueue cancelAllOperations];
-    [moveNetworkQueue release];
-    [copyNetworkQueue cancelAllOperations];
-    [copyNetworkQueue release];
-    [deleteNetworkQueue cancelAllOperations];
-    [deleteNetworkQueue release];
-    [uploadNetworkQueue cancelAllOperations];
-    [uploadNetworkQueue release];
-    [downloadNetworkQueue cancelAllOperations];
-    [downloadNetworkQueue release];
-    [refreshTimer invalidate];
-    [refreshTimer release];
-    [clipboardParentNode release];
-    [clipboardNodes release];
-    [draggedParentNode release];
-    [draggedNodes release];
-    [sharedPreviewController release];
-    [othersSharedNode release];
-    [mySharedNode release];
-    [sharedNode release];
-    [containersNodeChildren release];
-    [containersNode release];
-    [accountNode release];
-    [rootNode release];
-    [pithos release];
-    [super dealloc];
+- (void)windowDidLoad {
+    [super windowDidLoad];
+    if (browser && !browserInitialized) {
+        browserInitialized = YES;
+        [self initBrowser];
+    }
 }
 
 - (void)initBrowser {
@@ -172,27 +145,63 @@
     
     moveNetworkQueue = [[ASINetworkQueue alloc] init];
     moveNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
-    [moveNetworkQueue go];
+//    moveNetworkQueue.maxConcurrentOperationCount = 1;
     copyNetworkQueue = [[ASINetworkQueue alloc] init];
     copyNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
-    [copyNetworkQueue go];
+//    copyNetworkQueue.maxConcurrentOperationCount = 1;
     deleteNetworkQueue = [[ASINetworkQueue alloc] init];
     deleteNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
-    [deleteNetworkQueue go];
+//    deleteNetworkQueue.maxConcurrentOperationCount = 1;
     uploadNetworkQueue = [[ASINetworkQueue alloc] init];
     uploadNetworkQueue.showAccurateProgress = YES;
     uploadNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
-    [uploadNetworkQueue go];
+//    uploadNetworkQueue.maxConcurrentOperationCount = 1;
     downloadNetworkQueue = [[ASINetworkQueue alloc] init];
     downloadNetworkQueue.showAccurateProgress = YES;
     downloadNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
-    [downloadNetworkQueue go];
+//    downloadNetworkQueue.maxConcurrentOperationCount = 1;
+    
+    moveQueue = [[NSOperationQueue alloc] init];
+    [moveQueue setSuspended:YES];
+    moveQueue.name = @"gr.grnet.pithos.MoveQueue";
+//    moveQueue.maxConcurrentOperationCount = 1;
+    copyQueue = [[NSOperationQueue alloc] init];
+    [copyQueue setSuspended:YES];
+    copyQueue.name = @"gr.grnet.pithos.CopyQueue";
+//    copyQueue.maxConcurrentOperationCount = 1;
+    deleteQueue = [[NSOperationQueue alloc] init];
+    [deleteQueue setSuspended:YES];
+    deleteQueue.name = @"gr.grnet.pithos.DeleteQueue";
+//    deleteQueue.maxConcurrentOperationCount = 1;
+    uploadQueue = [[NSOperationQueue alloc] init];
+    [uploadQueue setSuspended:YES];
+    uploadQueue.name = @"gr.grnet.pithos.UploadQueue";
+//    uploadQueue.maxConcurrentOperationCount = 1;
+    downloadQueue = [[NSOperationQueue alloc] init];
+    [downloadQueue setSuspended:YES];
+    downloadQueue.name = @"gr.grnet.pithos.DownloadQueue";
+//    downloadQueue.maxConcurrentOperationCount = 1;
     
-    moveQueue = dispatch_queue_create("gr.grnet.pithos.MoveQueue", NULL);
-    copyQueue = dispatch_queue_create("gr.grnet.pithos.CopyQueue", NULL);
-    deleteQueue = dispatch_queue_create("gr.grnet.pithos.DeleteQueue", NULL);
-    uploadQueue = dispatch_queue_create("gr.grnet.pithos.UploadQueue", NULL);
-    downloadQueue = dispatch_queue_create("gr.grnet.pithos.DownloadQueue", NULL);
+    moveCallbackQueue = [[NSOperationQueue alloc] init];
+    [moveCallbackQueue setSuspended:YES];
+    moveCallbackQueue.name = @"gr.grnet.pithos.MoveCallbackQueue";
+//    moveCallbackQueue.maxConcurrentOperationCount = 1;
+    copyCallbackQueue = [[NSOperationQueue alloc] init];
+    [copyCallbackQueue setSuspended:YES];
+    copyCallbackQueue.name = @"gr.grnet.pithos.CopyCallbackQueue";
+//    copyCallbackQueue.maxConcurrentOperationCount = 1;
+    deleteCallbackQueue = [[NSOperationQueue alloc] init];
+    [deleteCallbackQueue setSuspended:YES];
+    deleteCallbackQueue.name = @"gr.grnet.pithos.DeleteCallbackQueue";
+//    deleteCallbackQueue.maxConcurrentOperationCount = 1;
+    uploadCallbackQueue = [[NSOperationQueue alloc] init];
+    [uploadCallbackQueue setSuspended:YES];
+    uploadCallbackQueue.name = @"gr.grnet.pithos.UploadCallbackQueue";
+//    uploadCallbackQueue.maxConcurrentOperationCount = 1;
+    downloadCallbackQueue = [[NSOperationQueue alloc] init];
+    [downloadCallbackQueue setSuspended:YES];
+    downloadCallbackQueue.name = @"gr.grnet.pithos.DownloadCallbackQueue";
+//    downloadCallbackQueue.maxConcurrentOperationCount = 1;
     
     [activityProgressIndicator setUsesThreadedAnimation:YES];
     [activityProgressIndicator setMinValue:0.0];
@@ -247,59 +256,182 @@
 }
 
 - (void)resetBrowser {
+    @synchronized(self) {
+        if (!browserActive)
+            return;
+    }
+
     [refreshTimer invalidate];
     [refreshTimer release];
     
-    [moveNetworkQueue cancelAllOperations];
-    [copyNetworkQueue cancelAllOperations];
-    [deleteNetworkQueue cancelAllOperations];
-    [uploadNetworkQueue cancelAllOperations];
-    [downloadNetworkQueue cancelAllOperations];
+    [moveNetworkQueue reset];
+    [copyNetworkQueue reset];
+    [deleteNetworkQueue reset];
+    [uploadNetworkQueue reset];
+    [downloadNetworkQueue reset];
     
+    [moveQueue cancelAllOperations];
+    [moveQueue setSuspended:YES];
+    [copyQueue cancelAllOperations];
+    [copyQueue setSuspended:YES];
+    [deleteQueue cancelAllOperations];
+    [deleteQueue setSuspended:YES];
+    [uploadQueue cancelAllOperations];
+    [uploadQueue setSuspended:YES];
+    [downloadQueue cancelAllOperations];
+    [downloadQueue setSuspended:YES];
+
+    [moveCallbackQueue cancelAllOperations];
+    [moveCallbackQueue setSuspended:YES];
+    [copyCallbackQueue cancelAllOperations];
+    [copyCallbackQueue setSuspended:YES];
+    [deleteCallbackQueue cancelAllOperations];
+    [deleteCallbackQueue setSuspended:YES];
+    [uploadCallbackQueue cancelAllOperations];
+    [uploadCallbackQueue setSuspended:YES];
+    [downloadCallbackQueue cancelAllOperations];
+    [downloadCallbackQueue setSuspended:YES];
+
     rootNode = nil;
     [browser loadColumnZero];
     [containersNodeChildren removeAllObjects];
     [outlineView reloadData];
-	// Expand the folder outline view
+    // Expand the folder outline view
     [outlineView expandItem:nil expandChildren:YES];
-	[outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
+    [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
     
     activityFacility.delegate = nil;
     [activityProgressIndicator setDoubleValue:1.0];
     [activityProgressIndicator stopAnimation:self];
+    
+    @synchronized(self) {
+        browserActive = NO;
+    }
 }
 
 - (void)startBrowser {
+    @synchronized(self) {
+        if (browserActive)
+            return;
+    }
+    
+    // In the improbable case of leftover operations
+    [moveNetworkQueue reset];
+    [copyNetworkQueue reset];
+    [deleteNetworkQueue reset];
+    [uploadNetworkQueue reset];
+    [downloadNetworkQueue reset];
+    [moveQueue cancelAllOperations];
+    [copyQueue cancelAllOperations];
+    [deleteQueue cancelAllOperations];
+    [uploadQueue cancelAllOperations];
+    [downloadQueue cancelAllOperations];
+    [moveCallbackQueue cancelAllOperations];
+    [copyCallbackQueue cancelAllOperations];
+    [deleteCallbackQueue cancelAllOperations];
+    [uploadCallbackQueue cancelAllOperations];
+    [downloadCallbackQueue cancelAllOperations];
+
+    [moveNetworkQueue go];
+    [copyNetworkQueue go];
+    [deleteNetworkQueue go];
+    [uploadNetworkQueue go];
+    [downloadNetworkQueue go];
+    [moveQueue setSuspended:NO];
+    [copyQueue setSuspended:NO];
+    [deleteQueue setSuspended:NO];
+    [uploadQueue setSuspended:NO];
+    [downloadQueue setSuspended:NO];
+    [moveCallbackQueue setSuspended:NO];
+    [copyCallbackQueue setSuspended:NO];
+    [deleteCallbackQueue setSuspended:NO];
+    [uploadCallbackQueue setSuspended:NO];
+    [downloadCallbackQueue setSuspended:NO];
+
     accountNode.pithos = pithos;
-    [accountNode refresh];
+    [accountNode forceRefresh];
     mySharedNode.pithos = pithos;
-    [mySharedNode refresh];
+    [mySharedNode forceRefresh];
     othersSharedNode.pithos = pithos;
-    [othersSharedNode refresh];
-    
-    [activityFacility reset];
+    [othersSharedNode forceRefresh];
+            
+//    [activityFacility reset];
     activityFacility.delegate = self;
-    
-    refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(forceRefresh:) userInfo:self repeats:YES] retain];
+            
+    refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:30 
+                                                     target:self 
+                                                   selector:@selector(forceRefresh:) 
+                                                   userInfo:self 
+                                                    repeats:YES] retain];
+    @synchronized(self) {
+        browserActive = YES;
+    }
 }
 
-- (void)setPithos:(ASIPithos *)aPithos {
-    if (aPithos && [aPithos isNotEqualTo:pithos]) {
-        [self resetBrowser];
-        [pithos release];
-        pithos = [aPithos retain];
-        [self startBrowser];
-    }
+- (BOOL)operationsPending {
+    return ([moveNetworkQueue operationCount] || 
+            [copyNetworkQueue operationCount] || 
+            [deleteNetworkQueue operationCount] || 
+            [uploadNetworkQueue operationCount] || 
+            [downloadNetworkQueue operationCount] || 
+            [moveQueue operationCount] || 
+            [copyQueue operationCount] || 
+            [deleteQueue operationCount] || 
+            [uploadQueue operationCount] || 
+            [downloadQueue operationCount] || 
+            [moveCallbackQueue operationCount] || 
+            [copyCallbackQueue operationCount] || 
+            [deleteCallbackQueue operationCount] || 
+            [uploadCallbackQueue operationCount] || 
+            [downloadCallbackQueue operationCount]);
 }
 
-- (void)windowDidLoad {
-    [super windowDidLoad];
-    if (browser && !browserInitialized) {
-        browserInitialized = YES;
-        [self initBrowser];
+- (void)dealloc {
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+    [self resetBrowser];
+    [moveQueue release];
+    [copyQueue release];
+    [deleteQueue release];
+    [uploadQueue release];
+    [downloadQueue release];
+    [moveNetworkQueue release];
+    [copyNetworkQueue release];
+    [deleteNetworkQueue release];
+    [uploadNetworkQueue release];
+    [downloadNetworkQueue release];
+    [clipboardParentNode release];
+    [clipboardNodes release];
+    [draggedParentNode release];
+    [draggedNodes release];
+    [sharedPreviewController release];
+    [othersSharedNode release];
+    [mySharedNode release];
+    [sharedNode release];
+    [containersNodeChildren release];
+    [containersNode release];
+    [accountNode release];
+    [rootNode release];
+    [pithos release];
+    [super dealloc];
+}
+
+- (void)setPithos:(ASIPithos *)aPithos {
+    if (aPithos) {
+        if (![aPithos.authUser isEqualToString:pithos.authUser] || 
+            ![aPithos.authToken isEqualToString:pithos.authToken] || 
+            ![aPithos.storageURLPrefix isEqualToString:pithos.storageURLPrefix] ||
+            ![aPithos.publicURLPrefix isEqualToString:pithos.publicURLPrefix]) {
+            [self resetBrowser];
+            [pithos release];
+            pithos = [aPithos retain];
+            [self startBrowser];
+        } else {
+            [self startBrowser];
+        }
     }
 }
 
+
 #pragma mark -
 #pragma mark Observers
 
@@ -347,12 +479,13 @@
         // Create pithos node
         ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithPithos:pithos 
                                                                                                             containerName:@"pithos"];
-        [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
-        while (![containerRequest isFinished]) {
-            usleep(1);
-        }
+        ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
+        [networkQueue go];
+        [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES];
         if ([containerRequest error]) {
-            [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
+            });
         } else {
             refreshAccountNode = YES;
         }
@@ -361,12 +494,13 @@
         // Create trash node
         ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest createOrUpdateContainerRequestWithPithos:pithos 
                                                                                                             containerName:@"trash"];
-        [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
-        while (![containerRequest isFinished]) {
-            usleep(1);
-        }
+        ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
+        [networkQueue go];
+        [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES];
         if ([containerRequest error]) {
-            [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
+            });
         } else {
             refreshAccountNode = YES;
         }
@@ -380,7 +514,7 @@
     // Expand the folder outline view
     [outlineView expandItem:nil expandChildren:YES];
     
-    if ((rootNode == nil) || (rootNode == containersNode) || (rootNode == sharedNode)) {
+    if (((rootNode == nil) || (rootNode == containersNode) || (rootNode == sharedNode)) && [containersNodeChildren count]) {
         rootNode = [containersNodeChildren objectAtIndex:0];
         [browser loadColumnZero];
     }
@@ -489,8 +623,13 @@
     }
     if (([node class] == [PithosObjectNode class]) || 
         (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
-        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        // Operation: Rename (move) an object or subdir/ node
+        __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
             NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
             if ([newName hasSuffix:@"/"])
                 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
@@ -502,24 +641,30 @@
                                                 error:&error 
                                           isDirectory:&isDirectory 
                                        sharingAccount:nil]) {
-                NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-                [alert setMessageText:@"Name Taken"];
-                [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
-                [alert addButtonWithTitle:@"OK"];
-                [alert runModal];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+                    [alert setMessageText:@"Name Taken"];
+                    [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
+                    [alert addButtonWithTitle:@"OK"];
+                    [alert runModal];
+                });
                 [pool drain];
                 return;
             } else if (error) {
                 [pool drain];
                 return;
             }
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
             ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos 
                                                                                    containerName:node.pithosContainer.name 
                                                                                       objectName:node.pithosObject.name 
                                                                         destinationContainerName:node.pithosContainer.name 
                                                                            destinationObjectName:destinationObjectName 
                                                                                    checkIfExists:NO];
-            if (objectRequest) {
+            if (!operation.isCancelled && objectRequest) {
                 objectRequest.delegate = self;
                 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -543,17 +688,24 @@
                   NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
                   NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                   moveNetworkQueue, @"networkQueue", 
-                  @"moveQueue", @"queueType", 
+                  @"move", @"operationType", 
                   nil]];
                 [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
             }
             [pool drain];
-        });
+        }];
+        [moveQueue addOperation:operation];
     } else if ([node class] == [PithosSubdirNode class]) {
         if (firstSlashRange.length == 1)
             return;
-        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        // Operation: Rename (move) a subdir node and its descendants
+        // The resulting ASIPithosObjectRequests are chained through dependencies
+        __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
             NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
             NSError *error = nil;
             BOOL isDirectory;
@@ -563,17 +715,23 @@
                                                 error:&error 
                                           isDirectory:&isDirectory 
                                        sharingAccount:nil]) {
-                NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-                [alert setMessageText:@"Name Taken"];
-                [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
-                [alert addButtonWithTitle:@"OK"];
-                [alert runModal];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+                    [alert setMessageText:@"Name Taken"];
+                    [alert setInformativeText:[NSString stringWithFormat:@"The name '%@' is already taken. Please choose a different name", newName]];
+                    [alert addButtonWithTitle:@"OK"];
+                    [alert runModal];
+                });
                 [pool drain];
                 return;
             } else if (error) {
                 [pool drain];
                 return;
             }
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
             if (node.pithosObject.subdir)
                 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
             NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos 
@@ -582,8 +740,13 @@
                                                                     destinationContainerName:node.pithosContainer.name 
                                                                        destinationObjectName:destinationObjectName 
                                                                                checkIfExists:NO];
-            if (objectRequests) {
+            if (!operation.isCancelled && objectRequests) {
+                ASIPithosObjectRequest *previousObjectRequest = nil;
                 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
+                    if (operation.isCancelled) {
+                        [pool drain];
+                        return;
+                    }
                     objectRequest.delegate = self;
                     objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                     objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -607,13 +770,17 @@
                       NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
                       NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                       moveNetworkQueue, @"networkQueue", 
-                      @"moveQueue", @"queueType", 
+                      @"move", @"operationType", 
                       nil]];
+                    if (previousObjectRequest)
+                        [objectRequest addDependency:previousObjectRequest];
+                    previousObjectRequest = objectRequest;
                     [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                 }
             }
             [pool drain];
-        });
+        }];
+        [moveQueue addOperation:operation];
     }
 }
 
@@ -664,16 +831,27 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
             [alert addButtonWithTitle:@"Cancel"];
             NSInteger choice = [alert runModal];
             if (choice == NSAlertFirstButtonReturn) {
-                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                // Operation: Download a subdir node and its descendants
+                // The resulting ASIPithosObjectRequests are chained through dependencies
+                __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                    if (operation.isCancelled) {
+                        [pool drain];
+                        return;
+                    }
                     NSArray *objectRequests = [PithosUtilities objectDataRequestsForSubdirWithPithos:pithos 
                                                                                        containerName:node.pithosContainer.name 
                                                                                           objectName:node.pithosObject.name 
                                                                                          toDirectory:[dropDestination path] 
                                                                                        checkIfExists:YES 
                                                                                       sharingAccount:node.sharingAccount];
-                    if (objectRequests) {
+                    if (!operation.isCancelled && objectRequests) {
+                        ASIPithosObjectRequest *previousObjectRequest = nil;
                         for (__block ASIPithosObjectRequest *objectRequest in objectRequests) {
+                            if (operation.isCancelled) {
+                                [pool drain];
+                                return;
+                            }
                             [names addObject:[objectRequest.userInfo objectForKey:@"fileName"]];
                             objectRequest.delegate = self;
                             objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
@@ -697,7 +875,7 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                               NSStringFromSelector(@selector(downloadObjectFinished:)), @"didFinishSelector", 
                               NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                               downloadNetworkQueue, @"networkQueue", 
-                              @"downloadQueue", @"queueType", 
+                              @"download", @"operationType", 
                               nil]];
                             [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
                                 [activityFacility updateActivity:activity 
@@ -705,22 +883,31 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                       totalBytes:activity.totalBytes 
                                                     currentBytes:(activity.currentBytes + size)];
                             }];
+                            if (previousObjectRequest)
+                                [objectRequest addDependency:previousObjectRequest];
+                            previousObjectRequest = objectRequest;
                             [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
                         }
                     }
                     [pool drain];
-                });
+                }];
+                [downloadQueue addOperation:operation];
             }
         } else {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Download an object node
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectDataRequestWithPithos:pithos 
                                                                                                containerName:node.pithosContainer.name 
                                                                                                   objectName:node.pithosObject.name 
                                                                                                  toDirectory:[dropDestination path] 
                                                                                                checkIfExists:YES 
                                                                                               sharingAccount:node.sharingAccount];
-                if (objectRequest) {
+                if (!operation.isCancelled && objectRequest) {
                     [names addObject:[objectRequest.userInfo objectForKey:@"fileName"]];
                     objectRequest.delegate = self;
                     objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
@@ -744,7 +931,7 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                       NSStringFromSelector(@selector(downloadObjectFinished:)), @"didFinishSelector", 
                       NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                       downloadNetworkQueue, @"networkQueue", 
-                      @"downloadQueue", @"queueType", 
+                      @"download", @"operationType", 
                       nil]];
                     [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
                         [activityFacility updateActivity:activity 
@@ -755,7 +942,8 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                     [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
                     [pool drain];
                 }
-            });
+            }];
+            [downloadQueue addOperation:operation];
         }
     }
     return names;
@@ -885,10 +1073,9 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
     if ((destinationNode.pithosContainer.blockHash == nil) || (destinationNode.pithosContainer.blockSize == 0)) {
         ASIPithosContainerRequest *containerRequest = [ASIPithosContainerRequest containerMetadataRequestWithPithos:pithos 
                                                                                                       containerName:containerName];
-        [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
-        while (![containerRequest isFinished]) {
-            usleep(1);
-        }
+        ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
+        [networkQueue go];
+        [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES];
         if ([containerRequest error]) {
             [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
             return NO;
@@ -908,8 +1095,13 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
             if (!isDirectory) {
                 // Upload file
                 NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
-                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                // Operation: Upload a local file
+                __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                    if (operation.isCancelled) {
+                        [pool drain];
+                        return;
+                    }
                     NSError *error = nil;
                     NSString *contentType = [PithosUtilities contentTypeOfFile:filePath error:&error];
                     if (contentType == nil)
@@ -917,6 +1109,10 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                     if (error)
                         NSLog(@"contentType detection error: %@", error);
                     NSArray *hashes = nil;
+                    if (operation.isCancelled) {
+                        [pool drain];
+                        return;
+                    }
                     ASIPithosObjectRequest *objectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos 
                                                                                                 containerName:containerName 
                                                                                                    objectName:objectName 
@@ -927,7 +1123,7 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                                 checkIfExists:YES 
                                                                                                        hashes:&hashes 
                                                                                                sharingAccount:destinationNode.sharingAccount];
-                    if (objectRequest) {
+                    if (!operation.isCancelled && objectRequest) {
                         objectRequest.delegate = self;
                         objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                         objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -957,14 +1153,15 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                           NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", 
                           NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                           uploadNetworkQueue, @"networkQueue", 
-                          @"uploadQueue", @"queueType", 
+                          @"upload", @"operationType", 
                           nil]];
                         if (destinationNode.sharingAccount)
                             [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
                         [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
                     }
                     [pool drain];
-                });
+                }];
+                [uploadQueue addOperation:operation];
             } else {
                 // Upload directory, confirm first
                 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
@@ -975,8 +1172,14 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                 NSInteger choice = [alert runModal];
                 if (choice == NSAlertFirstButtonReturn) {
                     NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
-                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                    // Operation: Upload a local directory and its descendants
+                    // The resulting ASIPithosObjectRequests are chained through dependencies
+                    __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                        if (operation.isCancelled) {
+                            [pool drain];
+                            return;
+                        }
                         NSMutableArray *objectNames = nil;
                         NSMutableArray *contentTypes = nil;
                         NSMutableArray *filePaths = nil;
@@ -995,7 +1198,16 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                         hashesArrays:&hashesArrays 
                                                                              directoryObjectRequests:&directoryObjectRequests 
                                                                                       sharingAccount:destinationNode.sharingAccount];
+                        if (operation.isCancelled) {
+                            [pool drain];
+                            return;
+                        }
+                        ASIPithosObjectRequest *previousObjectRequest = nil;
                         for (ASIPithosObjectRequest *objectRequest in directoryObjectRequests) {
+                            if (operation.isCancelled) {
+                                [pool drain];
+                                return;
+                            }
                             objectRequest.delegate = self;
                             objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                             objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -1014,12 +1226,19 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                               NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
                               NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                               uploadNetworkQueue, @"networkQueue", 
-                              @"uploadQueue", @"queueType", 
+                              @"upload", @"queue", 
                               nil]];
+                            if (previousObjectRequest)
+                                [objectRequest addDependency:previousObjectRequest];
+                            previousObjectRequest = objectRequest;
                             [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
                         }
-                        if (objectRequests) {
+                        if (!operation.isCancelled && objectRequests) {
                             for (NSUInteger i = 0 ; i < [objectRequests count] ; i++) {
+                                if (operation.isCancelled) {
+                                    [pool drain];
+                                    return;
+                                }
                                 ASIPithosObjectRequest *objectRequest = [objectRequests objectAtIndex:i];
                                 objectRequest.delegate = self;
                                 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
@@ -1049,15 +1268,19 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                   NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", 
                                   NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                                   uploadNetworkQueue, @"networkQueue", 
-                                  @"uploadQueue", @"queueType", 
+                                  @"upload", @"queue", 
                                   nil]];
                                 if (destinationNode.sharingAccount)
                                     [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
+                                if (previousObjectRequest)
+                                    [objectRequest addDependency:previousObjectRequest];
+                                previousObjectRequest = objectRequest;
                                 [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
                             }
                         }
                         [pool drain];
-                    });
+                    }];
+                    [uploadQueue addOperation:operation];
                 }
             }
         }
@@ -1079,8 +1302,13 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
     for (PithosNode *node in nodes) {
         if (([node class] == [PithosObjectNode class]) || 
             (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Move an object or subdir/ node
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
                 if ([node.pithosObject.name hasSuffix:@"/"])
                     destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
@@ -1090,7 +1318,7 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                             destinationContainerName:containerName 
                                                                                destinationObjectName:destinationObjectName 
                                                                                        checkIfExists:YES];
-                if (objectRequest) {
+                if (!operation.isCancelled && objectRequest) {
                     objectRequest.delegate = self;
                     objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                     objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -1113,15 +1341,22 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                       NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
                       NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                       moveNetworkQueue, @"networkQueue", 
-                      @"moveQueue", @"queueType", 
+                      @"move", @"operationType", 
                       nil]];
                     [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                 }
                 [pool drain];
-            });
+            }];
+            [moveQueue addOperation:operation];
         } else if ([node class] == [PithosSubdirNode class]) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Move a subdir node and its descendants
+            // The resulting ASIPithosObjectRequests are chained through dependencies
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
                 if (node.pithosObject.subdir)
                     destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
@@ -1131,8 +1366,13 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                         destinationContainerName:containerName 
                                                                            destinationObjectName:destinationObjectName 
                                                                                    checkIfExists:YES];
-                if (objectRequests) {
+                if (!operation.isCancelled && objectRequests) {
+                    ASIPithosObjectRequest *previousObjectRequest = nil;
                     for (ASIPithosObjectRequest *objectRequest in objectRequests) {
+                        if (operation.isCancelled) {
+                            [pool drain];
+                            return;
+                        }
                         objectRequest.delegate = self;
                         objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                         objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -1156,13 +1396,17 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                           NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
                           NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                           moveNetworkQueue, @"networkQueue", 
-                          @"moveQueue", @"queueType", 
+                          @"move", @"operationType", 
                           nil]];
+                        if (previousObjectRequest)
+                            [objectRequest addDependency:previousObjectRequest];
+                        previousObjectRequest = objectRequest;
                         [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                     }
                 }
                 [pool drain];
-            });
+            }];
+            [moveQueue addOperation:operation];
         }
     }
     return YES;
@@ -1182,8 +1426,13 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
     for (PithosNode *node in nodes) {
         if (([node class] == [PithosObjectNode class]) || 
             (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Copy an object or subdir/ node
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 NSString *destinationObjectName;
                 if (![destinationNode isEqualTo:node.parent]) {
                     destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
@@ -1194,6 +1443,10 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                        containerName:containerName 
                                                                           objectName:node.pithosObject.name];
                 }
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 ASIPithosObjectRequest *objectRequest = [PithosUtilities copyObjectRequestWithPithos:pithos 
                                                                                        containerName:node.pithosContainer.name 
                                                                                           objectName:node.pithosObject.name 
@@ -1201,7 +1454,7 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                destinationObjectName:destinationObjectName 
                                                                                        checkIfExists:YES 
                                                                                       sharingAccount:node.sharingAccount];
-                if (objectRequest) {
+                if (!operation.isCancelled && objectRequest) {
                     objectRequest.delegate = self;
                     objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                     objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -1224,15 +1477,22 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                       NSStringFromSelector(@selector(copyFinished:)), @"didFinishSelector", 
                       NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                       copyNetworkQueue, @"networkQueue", 
-                      @"copyQueue", @"queueType", 
+                      @"copy", @"operationType", 
                       nil]];
                     [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                 }
                 [pool drain];
-            });
+            }];
+            [copyQueue addOperation:operation];
         } else if ([node class] == [PithosSubdirNode class]) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Copy a subdir node and its descendants
+            // The resulting ASIPithosObjectRequests are chained through dependencies
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 NSString *destinationObjectName;
                 if (![destinationNode isEqualTo:node.parent]) {
                     destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
@@ -1243,6 +1503,10 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                        containerName:containerName 
                                                                           subdirName:node.pithosObject.name];
                 }
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 NSArray *objectRequests = [PithosUtilities copyObjectRequestsForSubdirWithPithos:pithos 
                                                                                    containerName:node.pithosContainer.name 
                                                                                       objectName:node.pithosObject.name 
@@ -1250,8 +1514,13 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                            destinationObjectName:destinationObjectName 
                                                                                    checkIfExists:YES 
                                                                                   sharingAccount:node.sharingAccount];
-                if (objectRequests) {
+                if (!operation.isCancelled && objectRequests) {
+                    ASIPithosObjectRequest *previousObjectRequest = nil;
                     for (ASIPithosObjectRequest *objectRequest in objectRequests) {
+                        if (operation.isCancelled) {
+                            [pool drain];
+                            return;
+                        }
                         objectRequest.delegate = self;
                         objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                         objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -1274,13 +1543,17 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                           NSStringFromSelector(@selector(copyFinished:)), @"didFinishSelector", 
                           NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                           copyNetworkQueue, @"networkQueue", 
-                          @"copyQueue", @"queueType", 
+                          @"copy", @"operationType", 
                           nil]];
+                        if (previousObjectRequest)
+                            [objectRequest addDependency:previousObjectRequest];
+                        previousObjectRequest = objectRequest;
                         [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                     }
                 }
                 [pool drain];
-            });
+            }];
+            [copyQueue addOperation:operation];
         }
     }
     return YES;
@@ -1290,47 +1563,93 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 #pragma mark ASIHTTPRequestDelegate
 
 - (void)performRequestFinishedDelegateInBackground:(ASIPithosRequest *)request {
-    dispatch_queue_t queue;
-    if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"moveQueue"])
-        queue = moveQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"copyQueue"])
-        queue = copyQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"deleteQueue"])
-        queue = deleteQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"uploadQueue"])
-        queue = uploadQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"downloadQueue"])
-        queue = downloadQueue;
+    NSOperationQueue *callbackQueue;
+    NSString *operationType = [request.userInfo objectForKey:@"operationType"];
+    if ([operationType isEqualToString:@"move"])
+        callbackQueue = moveCallbackQueue;
+    else if ([operationType isEqualToString:@"copy"])
+        callbackQueue = copyCallbackQueue;
+    else if ([operationType isEqualToString:@"delete"])
+        callbackQueue = deleteCallbackQueue;
+    else if ([operationType isEqualToString:@"upload"])
+        callbackQueue = uploadCallbackQueue;
+    else if ([operationType isEqualToString:@"download"])
+        callbackQueue = downloadCallbackQueue;
     else
-        return;
-    dispatch_async(queue, ^{
-        [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) withObject:request];
-    });
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
+                              withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+        });
+    // Add an operation to the callbackQueue with a completionBlock for the case of cancellation
+    NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self 
+                                                                             selector:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) 
+                                                                               object:request] autorelease];
+    operation.completionBlock = ^{
+        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+        if ([[request.userInfo objectForKey:@"operation"] isCancelled]) {
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
+                                  withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+            });
+        }
+        [pool drain];
+    };
+    [(NSMutableDictionary *)request.userInfo setObject:operation forKey:@"operation"];
+    [callbackQueue addOperation:operation];
 }
 
 - (void)performRequestFailedDelegateInBackground:(ASIPithosRequest *)request {
-    dispatch_queue_t queue;
-    if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"moveQueue"])
-        queue = moveQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"copyQueue"])
-        queue = copyQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"deleteQueue"])
-        queue = deleteQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"uploadQueue"])
-        queue = uploadQueue;
-    else if ([[request.userInfo objectForKey:@"queueType"] isEqualToString:@"downloadQueue"])
-        queue = downloadQueue;
-    else
-        return;
-    dispatch_async(queue, ^{
-        [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) withObject:request];
-    });
+    if (request.isCancelled) {
+        // Request has been cancelled 
+        // The callbackQueue might be suspended so we call directly the callback method, since it does minimal work anyway
+        [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) 
+                   withObject:request];
+    } else {
+        NSOperationQueue *callbackQueue;
+        NSString *operationType = [request.userInfo objectForKey:@"operationType"];
+        if ([operationType isEqualToString:@"move"])
+            callbackQueue = moveCallbackQueue;
+        else if ([operationType isEqualToString:@"copy"])
+            callbackQueue = copyCallbackQueue;
+        else if ([operationType isEqualToString:@"delete"])
+            callbackQueue = deleteCallbackQueue;
+        else if ([operationType isEqualToString:@"upload"])
+            callbackQueue = uploadCallbackQueue;
+        else if ([operationType isEqualToString:@"download"])
+            callbackQueue = downloadCallbackQueue;
+        else
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
+                                  withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+            });
+        // Add an operation to the callbackQueue with a completionBlock for the case of cancellation
+        NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self 
+                                                                                 selector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) 
+                                                                                   object:request] autorelease];
+        operation.completionBlock = ^{
+            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+            if ([[request.userInfo objectForKey:@"operation"] isCancelled]) {
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
+                                      withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+                });
+            }
+            [pool drain];
+        };
+        [(NSMutableDictionary *)request.userInfo setObject:operation forKey:@"operation"];
+        [callbackQueue addOperation:operation];
+    }
 }
 
 - (void)requestFailed:(ASIPithosRequest *)request {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [request.userInfo objectForKey:@"operation"];
     NSLog(@"Request failed: %@", request.url);
-    if ([request isCancelled]) {
+    if (operation.isCancelled) {
+        [pool drain];
+        return;        
+    }
+    if (request.isCancelled) {
         dispatch_async(dispatch_get_main_queue(), ^{
             [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
                               withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
@@ -1359,8 +1678,11 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 
 - (void)downloadObjectFinished:(ASIPithosObjectRequest *)objectRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
     NSLog(@"Download finished: %@", objectRequest.url);
-    if (objectRequest.responseStatusCode == 200) {
+    if (operation.isCancelled) {
+        [self requestFailed:objectRequest];
+    } else if (objectRequest.responseStatusCode == 200) {
         NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"];
         PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
         NSUInteger totalBytes = activity.totalBytes;
@@ -1400,8 +1722,11 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 
 - (void)uploadDirectoryObjectFinished:(ASIPithosObjectRequest *)objectRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
     NSLog(@"Upload directory object finished: %@", objectRequest.url);
-    if (objectRequest.responseStatusCode == 201) {
+    if (operation.isCancelled) {
+        [self requestFailed:objectRequest];
+    } else if (objectRequest.responseStatusCode == 201) {
         NSLog(@"Directory object created: %@", objectRequest.url);
         dispatch_async(dispatch_get_main_queue(), ^{
             [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
@@ -1426,12 +1751,15 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 
 - (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
     NSLog(@"Upload using hashmap finished: %@", objectRequest.url);
     NSString *fileName = [objectRequest.userInfo objectForKey:@"fileName"];
     PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
     NSUInteger totalBytes = activity.totalBytes;
     NSUInteger currentBytes = activity.currentBytes;
-    if (objectRequest.responseStatusCode == 201) {
+    if (operation.isCancelled) {
+        [self requestFailed:objectRequest];
+    } else if (objectRequest.responseStatusCode == 201) {
         NSLog(@"Object created: %@", objectRequest.url);
         dispatch_async(dispatch_get_main_queue(), ^{
             [activityFacility endActivity:activity 
@@ -1510,11 +1838,14 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 
 - (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"];
     NSLog(@"Upload of missing block finished: %@", containerRequest.url);
     NSUInteger blockSize = [[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue];
     NSString *fileName = [containerRequest.userInfo objectForKey:@"fileName"];
     PithosActivity *activity = [containerRequest.userInfo objectForKey:@"activity"];
-    if (containerRequest.responseStatusCode == 202) {
+    if (operation.isCancelled) {
+        [self requestFailed:containerRequest];
+    } else if (containerRequest.responseStatusCode == 202) {
         NSIndexSet *missingBlocks = [containerRequest.userInfo objectForKey:@"missingBlocks"];
         NSUInteger missingBlockIndex = [[containerRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue];
         missingBlockIndex = [missingBlocks indexGreaterThanIndex:missingBlockIndex];
@@ -1569,8 +1900,11 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 
 - (void)moveFinished:(ASIPithosObjectRequest *)objectRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
     NSLog(@"Move object finished: %@", objectRequest.url);
-    if (objectRequest.responseStatusCode == 201) {
+    if (operation.isCancelled) {
+        [self requestFailed:objectRequest];
+    } else if (objectRequest.responseStatusCode == 201) {
         dispatch_async(dispatch_get_main_queue(), ^{
             [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
                               withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
@@ -1594,8 +1928,11 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 
 - (void)copyFinished:(ASIPithosObjectRequest *)objectRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
     NSLog(@"Copy object finished: %@", objectRequest.url);
-    if (objectRequest.responseStatusCode == 201) {
+    if (operation.isCancelled) {
+        [self requestFailed:objectRequest];
+    } else if (objectRequest.responseStatusCode == 201) {
         dispatch_async(dispatch_get_main_queue(), ^{
             [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
                               withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
@@ -1619,8 +1956,11 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 
 - (void)deleteObjectFinished:(ASIPithosObjectRequest *)objectRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
     NSLog(@"Delete object finished: %@", objectRequest.url);
-    if (objectRequest.responseStatusCode == 204) {
+    if (operation.isCancelled) {
+        [self requestFailed:objectRequest];
+    } else if (objectRequest.responseStatusCode == 204) {
         dispatch_async(dispatch_get_main_queue(), ^{
             [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
                               withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
@@ -1945,12 +2285,21 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 - (void)menuNewFolder:(NSMenuItem *)sender {
     PithosNode *node = (PithosNode *)[sender representedObject];
     if ([node class] == [PithosContainerNode class]) {
-        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        // Operation: Create (upload) a new root application/directory object
+        __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
             NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos 
                                                                   containerName:node.pithosContainer.name 
                                                                      subdirName:@"untitled folder"];
             NSString *fileName = [safeObjectName lastPathComponent];
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
             ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
                                                                                                containerName:node.pithosContainer.name 
                                                                                                   objectName:safeObjectName 
@@ -1982,19 +2331,29 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                       NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
                                       NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                                       uploadNetworkQueue, @"networkQueue", 
-                                      @"uploadQueue", @"queueType", 
+                                      @"upload", @"operationType", 
                                       nil];
             [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
             [pool drain];
-        });
+        }];
+        [uploadQueue addOperation:operation];
     } else if (([node class] == [PithosSubdirNode class]) && 
                (node.pithosObject.subdir || ![node.pithosObject.name hasSuffix:@"/"])) {
-        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        // Operation: Create (upload) a new aplication/directory object
+        __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
             NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos 
                                                                   containerName:node.pithosContainer.name 
                                                                      subdirName:[node.pithosObject.name stringByAppendingPathComponent:@"untitled folder"]];
             NSString *fileName = [safeObjectName lastPathComponent];
+            if (operation.isCancelled) {
+                [pool drain];
+                return;
+            }
             ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
                                                                                                containerName:node.pithosContainer.name 
                                                                                                   objectName:safeObjectName 
@@ -2026,11 +2385,12 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                       NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
                                       NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                                       uploadNetworkQueue, @"networkQueue", 
-                                      @"uploadQueue", @"queueType", 
+                                      @"upload", @"operationType", 
                                       nil];
             [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
             [pool drain];
-        });
+        }];
+        [uploadQueue addOperation:operation];
     }
 }
 
@@ -2044,14 +2404,23 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
     for (PithosNode *node in ((NSArray *)[sender representedObject])) {
         if (([node class] == [PithosObjectNode class]) || 
             (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Delete an object or subdir/ node
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 NSString *fileName = [node.pithosObject.name lastPathComponent];
                 if ([node.pithosObject.name hasSuffix:@"/"])
                     fileName = [fileName stringByAppendingString:@"/"];
                 ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos 
                                                                                                 containerName:node.pithosContainer.name 
                                                                                                    objectName:node.pithosObject.name];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 objectRequest.delegate = self;
                 objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -2070,19 +2439,31 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                           NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", 
                                           NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                                           deleteNetworkQueue, @"networkQueue", 
-                                          @"deleteQueue", @"queueType", 
+                                          @"delete", @"operationType", 
                                           nil];
                 [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                 [pool drain];
-            });
+            }];
+            [deleteQueue addOperation:operation];
         } else if ([node class] == [PithosSubdirNode class]) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Delete a subdir node and its descendants
+            // The resulting ASIPithosObjectRequests are chained through dependencies
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 NSArray *objectRequests = [PithosUtilities deleteObjectRequestsForSubdirWithPithos:pithos 
                                                                                      containerName:node.pithosContainer.name 
                                                                                         objectName:node.pithosObject.name];
-                if (objectRequests) {
+                if (!operation.isCancelled && objectRequests) {
+                    ASIPithosObjectRequest *previousObjectRequest = nil;
                     for (ASIPithosObjectRequest *objectRequest in objectRequests) {
+                        if (operation.isCancelled) {
+                            [pool drain];
+                            return;
+                        }
                         objectRequest.delegate = self;
                         objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                         objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -2101,13 +2482,17 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                           NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", 
                           NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                           deleteNetworkQueue, @"networkQueue", 
-                          @"deleteQueue", @"queueType", 
+                          @"delete", @"operationType", 
                           nil]];
+                        if (previousObjectRequest)
+                            [objectRequest addDependency:previousObjectRequest];
+                        previousObjectRequest = objectRequest;
                         [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                     }
                 }
                 [pool drain];
-            });
+            }];
+            [deleteQueue addOperation:operation];
         }
     }
 }
@@ -2118,19 +2503,24 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
             (([node class] == [PithosSubdirNode class]) && 
              !node.pithosObject.subdir &&
              [node.pithosObject.name hasSuffix:@"/"])) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Move to trash an object or subdir/ node
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 NSString *safeObjectName = [PithosUtilities safeObjectNameForPithos:pithos 
                                                                       containerName:@"trash" 
                                                                          objectName:node.pithosObject.name];
-                if (safeObjectName) {
+                if (!operation.isCancelled && safeObjectName) {
                     ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithPithos:pithos 
                                                                                            containerName:node.pithosContainer.name 
                                                                                               objectName:node.pithosObject.name 
                                                                                 destinationContainerName:@"trash" 
                                                                                    destinationObjectName:safeObjectName 
                                                                                            checkIfExists:NO];
-                    if (objectRequest) {
+                    if (!operation.isCancelled && objectRequest) {
                         objectRequest.delegate = self;
                         objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                         objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -2153,28 +2543,40 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                           NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
                           NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                           moveNetworkQueue, @"networkQueue", 
-                          @"moveQueue", @"queueType", 
+                          @"move", @"operationType", 
                           nil]];
                         [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                     }
                 }
                 [pool drain];
-            });
+            }];
+            [moveQueue addOperation:operation];
         } else if ([node class] == [PithosSubdirNode class]) {
-            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            // Operation: Move to trash a subdir node and its descendants
+            // The resulting ASIPithosObjectRequests are chained through dependencies
+            __block NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                if (operation.isCancelled) {
+                    [pool drain];
+                    return;
+                }
                 NSString *safeObjectName = [PithosUtilities safeSubdirNameForPithos:pithos 
                                                                       containerName:@"trash" 
                                                                          subdirName:node.pithosObject.name];
-                if (safeObjectName) {
+                if (!operation.isCancelled && safeObjectName) {
                     NSArray *objectRequests = [PithosUtilities moveObjectRequestsForSubdirWithPithos:pithos 
                                                                                        containerName:node.pithosContainer.name 
                                                                                           objectName:node.pithosObject.name 
                                                                             destinationContainerName:@"trash" 
                                                                                destinationObjectName:safeObjectName 
                                                                                        checkIfExists:NO];
-                    if (objectRequests) {
+                    if (!operation.isCancelled && objectRequests) {
+                        ASIPithosObjectRequest *previousObjectRequest = nil;
                         for (ASIPithosObjectRequest *objectRequest in objectRequests) {
+                            if (operation.isCancelled) {
+                                [pool drain];
+                                return;
+                            }
                             objectRequest.delegate = self;
                             objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                             objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
@@ -2197,14 +2599,18 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                               NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
                               NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                               moveNetworkQueue, @"networkQueue", 
-                              @"moveQueue", @"queueType", 
+                              @"move", @"operationType", 
                               nil]];
+                            if (previousObjectRequest)
+                                [objectRequest addDependency:previousObjectRequest];
+                            previousObjectRequest = objectRequest;
                             [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                         }
                     }
                 }
                 [pool drain];
-            });
+            }];
+            [moveQueue addOperation:operation];
         }
     }
 }
