Fix concurrency issues. Modify PithosBrowser to use ASINetworkQueues for ASIPithosReq...
authorMiltiadis Vasilakis <mvasilak@gmail.com>
Tue, 10 Jan 2012 21:55:13 +0000 (23:55 +0200)
committerMiltiadis Vasilakis <mvasilak@gmail.com>
Tue, 10 Jan 2012 22:24:48 +0000 (00:24 +0200)
pithos-apple-common
pithos-macos/PithosBrowserController.h
pithos-macos/PithosBrowserController.m

index 40db2cf..30650f5 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 40db2cf7a8a1aa88098e1e224b29a3570c806aa2
+Subproject commit 30650f5a979e5acaff4a85aa953298c83ce320a7
index 584826b..d6f758f 100644 (file)
@@ -42,6 +42,7 @@
 @class PithosSharingAccountsNode;
 @class PithosEmptyNode;
 @class PithosActivityFacility;
+@class ASINetworkQueue;
 
 @interface PithosBrowserController : NSWindowController <NSBrowserDelegate, NSSplitViewDelegate, NSOutlineViewDelegate, NSOutlineViewDataSource, NSMenuDelegate, PithosActivityFacilityDelegate> {
     PithosNode *rootNode;
     PithosActivityFacility *activityFacility;
     
     NSTimer *refreshTimer;
+    
+    ASINetworkQueue *moveNetworkQueue;
+    ASINetworkQueue *copyNetworkQueue;
+    ASINetworkQueue *deleteNetworkQueue;
+    ASINetworkQueue *uploadNetworkQueue;
+    ASINetworkQueue *downloadNetworkQueue;
+    dispatch_queue_t moveQueue;
+    dispatch_queue_t copyQueue;
+    dispatch_queue_t deleteQueue;
+    dispatch_queue_t uploadQueue;
+    dispatch_queue_t downloadQueue;
 }
 
 @property (nonatomic, retain) PithosAccountNode *accountNode;
index 38866d7..5e203e0 100644 (file)
@@ -45,6 +45,7 @@
 #import "PithosEmptyNode.h"
 #import "ImageAndTextCell.h"
 #import "FileSystemBrowserCell.h"
+#import "ASINetworkQueue.h"
 #import "ASIPithosRequest.h"
 #import "ASIPithosContainerRequest.h"
 #import "ASIPithosObjectRequest.h"
 
 - (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];
     [outlineView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
     
     [browser setCellClass:[PithosBrowserCell class]];
+    
+    moveNetworkQueue = [[ASINetworkQueue alloc] init];
+    moveNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
+    [moveNetworkQueue go];
+    copyNetworkQueue = [[ASINetworkQueue alloc] init];
+    copyNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
+    [copyNetworkQueue go];
+    deleteNetworkQueue = [[ASINetworkQueue alloc] init];
+    deleteNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
+    [deleteNetworkQueue go];
+    uploadNetworkQueue = [[ASINetworkQueue alloc] init];
+    uploadNetworkQueue.showAccurateProgress = YES;
+    uploadNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
+    [uploadNetworkQueue go];
+    downloadNetworkQueue = [[ASINetworkQueue alloc] init];
+    downloadNetworkQueue.showAccurateProgress = YES;
+    downloadNetworkQueue.shouldCancelAllRequestsOnFailure = NO;
+    [downloadNetworkQueue go];
+    
+    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);
 }
 
 - (void)resetContainers:(NSNotification *)notification {
     [refreshTimer invalidate];
     [refreshTimer release];
     
+    [moveNetworkQueue cancelAllOperations];
+    [copyNetworkQueue cancelAllOperations];
+    [deleteNetworkQueue cancelAllOperations];
+    [uploadNetworkQueue cancelAllOperations];
+    [downloadNetworkQueue cancelAllOperations];
+    
     rootNode = nil;
     [browser loadColumnZero];
     [containersNodeChildren removeAllObjects];
     }
     if (([node class] == [PithosObjectNode class]) || 
         (([node class] == [PithosSubdirNode class]) && !node.pithosObject.subdir && [node.pithosObject.name hasSuffix:@"/"])) {
-        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-        dispatch_async(queue, ^{
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
             NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
             if ([newName hasSuffix:@"/"])
                 destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
                 [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;
             }
             ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithContainerName:node.pithosContainer.name 
                                                                                           checkIfExists:NO];
             if (objectRequest) {
                 objectRequest.delegate = self;
-                objectRequest.didFinishSelector = @selector(moveFinished:);
-                objectRequest.didFailSelector = @selector(moveFailed:);
+                objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", 
+                                           [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
+                                           [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
+                                           [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
+                                           [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
                 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove 
-                                                                           message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", 
-                                                                                    [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
-                                                                                    [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
-                                                                                    [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
-                                                                                    [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
+                                                                           message:messagePrefix];
                 [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
                  [NSDictionary dictionaryWithObjectsAndKeys:
                   [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", 
                   [NSNumber numberWithBool:YES], @"refresh", 
                   activity, @"activity", 
+                  [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                  [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                  [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
+                  [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                   [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                  NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
+                  NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                  moveNetworkQueue, @"networkQueue", 
+                  @"moveQueue", @"queueType", 
                   nil]];
-                [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+                [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
             }
+            [pool drain];
         });
     } else if ([node class] == [PithosSubdirNode class]) {
         if (firstSlashRange.length == 1)
             return;
-        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-        dispatch_async(queue, ^{
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
             NSString *destinationObjectName = [[node.pithosObject.name stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];
             NSError *error = nil;
             BOOL isDirectory;
                 [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 (node.pithosObject.subdir)
             if (objectRequests) {
                 for (ASIPithosObjectRequest *objectRequest in objectRequests) {
                     objectRequest.delegate = self;
-                    objectRequest.didFinishSelector = @selector(moveFinished:);
-                    objectRequest.didFailSelector = @selector(moveFailed:);
+                    objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                    objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                    NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", 
+                                               [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
+                                               [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
+                                               [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
+                                               [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
                     PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove 
-                                                                               message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", 
-                                                                                        [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
-                                                                                        [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
-                                                                                        [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
-                                                                                        [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
+                                                                               message:messagePrefix];
                     [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
                      [NSDictionary dictionaryWithObjectsAndKeys:
                       [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", 
                       [NSNumber numberWithBool:YES], @"refresh", 
                       activity, @"activity", 
+                      [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                      [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                      [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
+                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                       [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                      NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
+                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                      moveNetworkQueue, @"networkQueue", 
+                      @"moveQueue", @"queueType", 
                       nil]];
-                    [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+                    [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                 }
             }
+            [pool drain];
         });
     }
 }
@@ -568,8 +638,8 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
             [alert addButtonWithTitle:@"Cancel"];
             NSInteger choice = [alert runModal];
             if (choice == NSAlertFirstButtonReturn) {
-                dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-                dispatch_async(queue, ^{
+                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                     NSArray *objectRequests = [PithosUtilities objectDataRequestsForSubdirWithContainerName:node.pithosContainer.name 
                                                                                                      objectName:node.pithosObject.name 
                                                                                                     toDirectory:[dropDestination path] 
@@ -577,58 +647,87 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                                  sharingAccount:node.sharingAccount];
                     if (objectRequests) {
                         for (__block ASIPithosObjectRequest *objectRequest in objectRequests) {
-                            [names addObject:[objectRequest.userInfo valueForKey:@"fileName"]];
+                            [names addObject:[objectRequest.userInfo objectForKey:@"fileName"]];
                             objectRequest.delegate = self;
-                            objectRequest.didFinishSelector = @selector(downloadObjectFinished:);
-                            objectRequest.didFailSelector = @selector(downloadObjectFailed:);
+                            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                            NSString *messagePrefix = [NSString stringWithFormat:@"Downloading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
                             PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload 
-                                                                                       message:[NSString stringWithFormat:@"Downloading '%@' (0%%)", [objectRequest.userInfo objectForKey:@"fileName"]] 
+                                                                                       message:[messagePrefix stringByAppendingString:@" (0%)"]
                                                                                     totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue] 
                                                                                   currentBytes:0];
+                            dispatch_async(dispatch_get_main_queue(), ^{
+                                [activityFacility updateActivity:activity withMessage:activity.message];  
+                            });
                             [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
                              [NSDictionary dictionaryWithObjectsAndKeys:
                               activity, @"activity", 
+                              [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                              [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                              [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", 
+                              [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
                               [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                              NSStringFromSelector(@selector(downloadObjectFinished:)), @"didFinishSelector", 
+                              NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                              downloadNetworkQueue, @"networkQueue", 
+                              @"downloadQueue", @"queueType", 
                               nil]];
                             [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
                                 [activityFacility updateActivity:activity 
-                                                     withMessage:[NSString stringWithFormat:@"Downloading '%@' (%.0f%%)", [objectRequest.userInfo valueForKey:@"fileName"], (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] 
+                                                     withMessage:[messagePrefix stringByAppendingFormat:@" (%.0f%%)", (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] 
                                                       totalBytes:activity.totalBytes 
                                                     currentBytes:(activity.currentBytes + size)];
                             }];
-                            [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
+                            [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
                         }
                     }
+                    [pool drain];
                 });
             }
         } else {
-            __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectDataRequestWithContainerName:node.pithosContainer.name 
-                                                                                                     objectName:node.pithosObject.name 
-                                                                                                    toDirectory:[dropDestination path] 
-                                                                                                  checkIfExists:YES 
-                                                                                                 sharingAccount:node.sharingAccount];
-            if (objectRequest) {
-                [names addObject:[objectRequest.userInfo valueForKey:@"fileName"]];
-                objectRequest.delegate = self;
-                objectRequest.didFinishSelector = @selector(downloadObjectFinished:);
-                objectRequest.didFailSelector = @selector(downloadObjectFailed:);
-                PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload 
-                                                                           message:[NSString stringWithFormat:@"Downloading '%@' (0%%)", [objectRequest.userInfo valueForKey:@"fileName"]]
-                                                                        totalBytes:node.pithosObject.bytes 
-                                                                      currentBytes:0];
-                [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
-                 [NSDictionary dictionaryWithObjectsAndKeys:
-                  activity, @"activity", 
-                  [NSNumber numberWithUnsignedInteger:10], @"retries", 
-                  nil]];
-                [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
-                    [activityFacility updateActivity:activity 
-                                         withMessage:[NSString stringWithFormat:@"Downloading '%@' (%.0f%%)", [objectRequest.userInfo valueForKey:@"fileName"], (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] 
-                                          totalBytes:activity.totalBytes 
-                                        currentBytes:(activity.currentBytes + size)];
-                }];
-                [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
-            }
+            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectDataRequestWithContainerName:node.pithosContainer.name 
+                                                                                                         objectName:node.pithosObject.name 
+                                                                                                        toDirectory:[dropDestination path] 
+                                                                                                      checkIfExists:YES 
+                                                                                                     sharingAccount:node.sharingAccount];
+                if (objectRequest) {
+                    [names addObject:[objectRequest.userInfo objectForKey:@"fileName"]];
+                    objectRequest.delegate = self;
+                    objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                    objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                    NSString *messagePrefix = [NSString stringWithFormat:@"Downloading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
+                    PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload 
+                                                                               message:[messagePrefix stringByAppendingString:@" (0%)"]
+                                                                            totalBytes:node.pithosObject.bytes 
+                                                                          currentBytes:0];
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        [activityFacility updateActivity:activity withMessage:activity.message];  
+                    });
+                    [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
+                     [NSDictionary dictionaryWithObjectsAndKeys:
+                      activity, @"activity", 
+                      [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                      [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                      [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", 
+                      [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
+                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                      NSStringFromSelector(@selector(downloadObjectFinished:)), @"didFinishSelector", 
+                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                      downloadNetworkQueue, @"networkQueue", 
+                      @"downloadQueue", @"queueType", 
+                      nil]];
+                    [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
+                        [activityFacility updateActivity:activity 
+                                             withMessage:[messagePrefix stringByAppendingFormat:@" (%.0f%%)", (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] 
+                                              totalBytes:activity.totalBytes 
+                                            currentBytes:(activity.currentBytes + size)];
+                    }];
+                    [downloadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
+                    [pool drain];
+                }
+            });
         }
     }
     return names;
@@ -780,8 +879,8 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
             if (!isDirectory) {
                 // Upload file
                 NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
-                dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-                dispatch_async(queue, ^{
+                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                     NSError *error = nil;
                     NSString *contentType = [PithosUtilities contentTypeOfFile:filePath error:&error];
                     if (contentType == nil)
@@ -800,11 +899,12 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                                       sharingAccount:destinationNode.sharingAccount];
                     if (objectRequest) {
                         objectRequest.delegate = self;
-                        objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
-                        objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
+                        objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                        objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                        NSString *messagePrefix = [NSString stringWithFormat:@"Uploading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
                         PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload 
-                                                                                   message:[NSString stringWithFormat:@"Uploading '%@' (0%%)", [objectRequest.userInfo valueForKey:@"fileName"]]
-                                                                                totalBytes:[[objectRequest.userInfo valueForKey:@"bytes"] unsignedIntegerValue]
+                                                                                   message:[messagePrefix stringByAppendingString:@" (0%)"]
+                                                                                totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue]
                                                                               currentBytes:0];
                         [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
                          [NSDictionary dictionaryWithObjectsAndKeys:
@@ -819,12 +919,21 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                           [NSNumber numberWithBool:YES], @"refresh", 
                           [NSNumber numberWithUnsignedInteger:10], @"iteration", 
                           activity, @"activity", 
+                          [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                          [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                          [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", 
+                          [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
                           [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                          NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", 
+                          NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                          uploadNetworkQueue, @"networkQueue", 
+                          @"uploadQueue", @"queueType", 
                           nil]];
                         if (destinationNode.sharingAccount)
                             [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
-                        [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
+                        [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
                     }
+                    [pool drain];
                 });
             } else {
                 // Upload directory, confirm first
@@ -836,8 +945,8 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                 NSInteger choice = [alert runModal];
                 if (choice == NSAlertFirstButtonReturn) {
                     NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
-                    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-                    dispatch_async(queue, ^{
+                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                         NSMutableArray *objectNames = nil;
                         NSMutableArray *contentTypes = nil;
                         NSMutableArray *filePaths = nil;
@@ -857,27 +966,37 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                                  sharingAccount:destinationNode.sharingAccount];
                         for (ASIPithosObjectRequest *objectRequest in directoryObjectRequests) {
                             objectRequest.delegate = self;
-                            objectRequest.didFinishSelector = @selector(uploadDirectoryObjectFinished:);
-                            objectRequest.didFailSelector = @selector(uploadDirectoryObjectFailed:);
+                            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                            NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
                             PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory 
-                                                                                       message:[NSString stringWithFormat:@"Creating directory '%@'", [objectRequest.userInfo valueForKey:@"fileName"]]];
+                                                                                       message:messagePrefix];
                             [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
                              [NSDictionary dictionaryWithObjectsAndKeys:
                               [NSNumber numberWithBool:YES], @"refresh", 
                               activity, @"activity", 
+                              [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                              [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                              [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
+                              [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
                               [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                              NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
+                              NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                              uploadNetworkQueue, @"networkQueue", 
+                              @"uploadQueue", @"queueType", 
                               nil]];
-                            [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
+                            [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
                         }
                         if (objectRequests) {
                             for (NSUInteger i = 0 ; i < [objectRequests count] ; i++) {
                                 ASIPithosObjectRequest *objectRequest = [objectRequests objectAtIndex:i];
                                 objectRequest.delegate = self;
-                                objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
-                                objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
+                                objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                                objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                                NSString *messagePrefix = [NSString stringWithFormat:@"Uploading '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
                                 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload 
-                                                                                           message:[NSString stringWithFormat:@"Uploading '%@' (0%%)", [objectRequest.userInfo valueForKey:@"fileName"]]
-                                                                                        totalBytes:[[objectRequest.userInfo valueForKey:@"bytes"] unsignedIntegerValue]
+                                                                                           message:[messagePrefix stringByAppendingString:@" (0%)"]
+                                                                                        totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue]
                                                                                       currentBytes:0];
                                 [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
                                  [NSDictionary dictionaryWithObjectsAndKeys:
@@ -891,13 +1010,22 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                   [NSNumber numberWithBool:YES], @"refresh", 
                                   [NSNumber numberWithUnsignedInteger:10], @"iteration", 
                                   activity, @"activity", 
+                                  [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                                  [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                                  [messagePrefix stringByAppendingString:@" (100%)"], @"finishedActivityMessage", 
+                                  [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
                                   [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                                  NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", 
+                                  NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                                  uploadNetworkQueue, @"networkQueue", 
+                                  @"uploadQueue", @"queueType", 
                                   nil]];
                                 if (destinationNode.sharingAccount)
                                     [(NSMutableDictionary *)objectRequest.userInfo setObject:destinationNode.sharingAccount forKey:@"sharingAccount"];
-                                [[PithosUtilities prepareRequest:objectRequest] startAsynchronous];
+                                [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest]];
                             }
                         }
+                        [pool drain];
                     });
                 }
             }
@@ -920,8 +1048,8 @@ 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_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-            dispatch_async(queue, ^{
+            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                 NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
                 if ([node.pithosObject.name hasSuffix:@"/"])
                     destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
@@ -932,26 +1060,36 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                                   checkIfExists:YES];
                 if (objectRequest) {
                     objectRequest.delegate = self;
-                    objectRequest.didFinishSelector = @selector(moveFinished:);
-                    objectRequest.didFailSelector = @selector(moveFailed:);
+                    objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                    objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                    NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", 
+                                               [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
+                                               [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
+                                               [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
+                                               [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
                     PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove 
-                                                                               message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", 
-                                                                                        [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
-                                                                                        [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
-                                                                                        [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
-                                                                                        [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
+                                                                               message:messagePrefix];
                     [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
                      [NSDictionary dictionaryWithObjectsAndKeys:
                       [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes", 
                       activity, @"activity", 
+                      [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                      [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                      [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
+                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                       [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                      NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
+                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                      moveNetworkQueue, @"networkQueue", 
+                      @"moveQueue", @"queueType", 
                       nil]];
-                    [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+                    [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                 }
+                [pool drain];
             });
         } else if ([node class] == [PithosSubdirNode class]) {
-            dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-            dispatch_async(queue, ^{
+            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                 NSString *destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
                 if (node.pithosObject.subdir)
                     destinationObjectName = [destinationObjectName stringByAppendingString:@"/"];
@@ -963,24 +1101,34 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                 if (objectRequests) {
                     for (ASIPithosObjectRequest *objectRequest in objectRequests) {
                         objectRequest.delegate = self;
-                        objectRequest.didFinishSelector = @selector(moveFinished:);
-                        objectRequest.didFailSelector = @selector(moveFailed:);
+                        objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                        objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                        NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", 
+                                                   [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
+                                                   [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
+                                                   [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
+                                                   [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
                         PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove 
-                                                                                   message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", 
-                                                                                            [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
-                                                                                            [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
-                                                                                            [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
-                                                                                            [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
+                                                                                   message:messagePrefix];
                         [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
                          [NSDictionary dictionaryWithObjectsAndKeys:
                           [NSArray arrayWithObjects:node.parent, destinationNode, nil], @"forceRefreshNodes", 
                           [NSNumber numberWithBool:YES], @"refresh", 
                           activity, @"activity", 
+                          [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                          [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                          [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
+                          [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                           [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                          NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
+                          NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                          moveNetworkQueue, @"networkQueue", 
+                          @"moveQueue", @"queueType", 
                           nil]];
-                        [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+                        [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                     }
                 }
+                [pool drain];
             });
         }
     }
@@ -1001,8 +1149,8 @@ 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_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-            dispatch_async(queue, ^{
+            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                 NSString *destinationObjectName;
                 if (![destinationNode isEqualTo:node.parent]) {
                     destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
@@ -1020,26 +1168,36 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                                  sharingAccount:node.sharingAccount];
                 if (objectRequest) {
                     objectRequest.delegate = self;
-                    objectRequest.didFinishSelector = @selector(copyFinished:);
-                    objectRequest.didFailSelector = @selector(copyFailed:);
+                    objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                    objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                    NSString *messagePrefix = [NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'", 
+                                               [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
+                                               [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
+                                               [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
+                                               [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
                     PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCopy 
-                                                                               message:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'", 
-                                                                                        [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
-                                                                                        [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
-                                                                                        [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
-                                                                                        [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
+                                                                               message:messagePrefix];
                     [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
                      [NSDictionary dictionaryWithObjectsAndKeys:
                       [NSArray arrayWithObject:destinationNode], @"forceRefreshNodes", 
                       activity, @"activity", 
+                      [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                      [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                      [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
+                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                       [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                      NSStringFromSelector(@selector(copyFinished:)), @"didFinishSelector", 
+                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                      copyNetworkQueue, @"networkQueue", 
+                      @"copyQueue", @"queueType", 
                       nil]];
-                    [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+                    [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                 }
+                [pool drain];
             });
         } else if ([node class] == [PithosSubdirNode class]) {
-            dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-            dispatch_async(queue, ^{
+            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                 NSString *destinationObjectName;
                 if (![destinationNode isEqualTo:node.parent]) {
                     destinationObjectName = [objectNamePrefix stringByAppendingPathComponent:node.displayName];
@@ -1058,23 +1216,33 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                 if (objectRequests) {
                     for (ASIPithosObjectRequest *objectRequest in objectRequests) {
                         objectRequest.delegate = self;
-                        objectRequest.didFinishSelector = @selector(copyFinished:);
-                        objectRequest.didFailSelector = @selector(copyFailed:);
+                        objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                        objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                        NSString *messagePrefix = [NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'", 
+                                                   [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
+                                                   [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
+                                                   [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
+                                                   [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
                         PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCopy 
-                                                                                   message:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@'", 
-                                                                                            [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
-                                                                                            [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
-                                                                                            [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
-                                                                                            [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
+                                                                                   message:messagePrefix];
                         [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
                          [NSDictionary dictionaryWithObjectsAndKeys:
                           [NSArray arrayWithObject:destinationNode], @"forceRefreshNodes", 
                           activity, @"activity", 
+                          [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                          [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                          [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
+                          [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                           [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                          NSStringFromSelector(@selector(copyFinished:)), @"didFinishSelector", 
+                          NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                          copyNetworkQueue, @"networkQueue", 
+                          @"copyQueue", @"queueType", 
                           nil]];
-                        [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+                        [copyNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                     }
                 }
+                [pool drain];
             });
         }
     }
@@ -1084,7 +1252,76 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 #pragma mark -
 #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;
+    else
+        return;
+    dispatch_async(queue, ^{
+        [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) withObject:request];
+    });
+}
+
+- (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];
+    });
+}
+
+- (void)requestFailed:(ASIPithosRequest *)request {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSLog(@"Request failed: %@", request.url);
+    if ([request isCancelled]) {
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
+                              withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+        });
+        [pool drain];
+        return;
+    }
+    NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
+    if (retries > 0) {
+        ASIPithosRequest *newRequest = (ASIPithosRequest *)[[PithosUtilities copyRequest:request] autorelease];
+        [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
+        [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithBool:NO] forKey:@"unexpectedResponseStatus"];
+        [[newRequest.userInfo objectForKey:@"networkQueue"] addOperation:[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]]];
+    } else {
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
+                              withMessage:[request.userInfo objectForKey:@"failedActivityMessage"]];
+            if ([[request.userInfo objectForKey:@"unexpectedResponseStatus"] boolValue])
+                [PithosUtilities unexpectedResponseStatusAlertWithRequest:request];
+            else
+                [PithosUtilities httpRequestErrorAlertWithRequest:request];
+        });
+    }
+    [pool drain];
+}
+
 - (void)downloadObjectFinished:(ASIPithosObjectRequest *)objectRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLog(@"Download finished: %@", objectRequest.url);
     if (objectRequest.responseStatusCode == 200) {
         NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"];
@@ -1097,11 +1334,13 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
             NSFileManager *fileManager = [NSFileManager defaultManager];
             if (![fileManager fileExistsAtPath:filePath]) {
                 if (![fileManager createFileAtPath:filePath contents:nil attributes:nil]) {
-                    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-                    [alert setMessageText:@"Create File Error"];
-                    [alert setInformativeText:[NSString stringWithFormat:@"Cannot create zero length file at %@", filePath]];
-                    [alert addButtonWithTitle:@"OK"];
-                    [alert runModal];
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+                        [alert setMessageText:@"Create File Error"];
+                        [alert setInformativeText:[NSString stringWithFormat:@"Cannot create zero length file at %@", filePath]];
+                        [alert addButtonWithTitle:@"OK"];
+                        [alert runModal];
+                    });
                 }
             }
         }
@@ -1109,48 +1348,28 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
         NSUInteger currentBytes = [objectRequest objectContentLength];
         if (currentBytes == 0)
             currentBytes = totalBytes;
-        [activityFacility endActivity:activity 
-                          withMessage:[NSString stringWithFormat:@"Downloading '%@' (100%%)", 
-                                       [objectRequest.userInfo objectForKey:@"fileName"]] 
-                           totalBytes:totalBytes 
-                         currentBytes:currentBytes];
-    } else {
-        NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
-        if (retries > 0) {
-            ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:objectRequest] autorelease];
-            [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-            [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
-        } else {
-            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                              withMessage:[NSString stringWithFormat:@"Downloading '%@' (failed)", 
-                                           [objectRequest.userInfo objectForKey:@"fileName"]]];
-            [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
-        }
-    }
-}
-
-- (void)downloadObjectFailed:(ASIPithosObjectRequest *)objectRequest {
-    NSLog(@"Download failed: %@", objectRequest.url);
-    NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
-    if (retries > 0) {
-        ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:objectRequest] autorelease];
-        [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-        [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:activity 
+                              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] 
+                               totalBytes:totalBytes 
+                             currentBytes:currentBytes];
+        });
     } else {
-        [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[NSString stringWithFormat:@"Downloading '%@' (failed)", 
-                                       [objectRequest.userInfo objectForKey:@"fileName"]]];
-        [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
+        [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
+        [self requestFailed:objectRequest];
     }
+    [pool drain];
 }
 
 - (void)uploadDirectoryObjectFinished:(ASIPithosObjectRequest *)objectRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLog(@"Upload directory object finished: %@", objectRequest.url);
     if (objectRequest.responseStatusCode == 201) {
         NSLog(@"Directory object created: %@", objectRequest.url);
-        [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[NSString stringWithFormat:@"Creating directory '%@' (finished)", 
-                                       [objectRequest.userInfo objectForKey:@"fileName"]]];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
+                              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+        });
         for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
             [node forceRefresh];
         }
@@ -1162,36 +1381,14 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
         else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
             [self refresh:self];
     } else {
-        NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
-        if (retries > 0) {
-            ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:objectRequest] autorelease];
-            [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-            [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
-        } else {
-            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                              withMessage:[NSString stringWithFormat:@"Creating directory '%@' (failed)", 
-                                           [objectRequest.userInfo objectForKey:@"fileName"]]];
-            [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
-        }
-    }
-}
-
-- (void)uploadDirectoryObjectFailed:(ASIPithosObjectRequest *)objectRequest {
-    NSLog(@"Upload directory object failed: %@", objectRequest.url);
-    NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
-    if (retries > 0) {
-        ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:objectRequest] autorelease];
-        [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-        [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
-    } else {
-        [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[NSString stringWithFormat:@"Creating directory '%@' (failed)", 
-                                       [objectRequest.userInfo objectForKey:@"fileName"]]];
-        [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
+        [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
+        [self requestFailed:objectRequest];
     }
+    [pool drain];
 }
 
 - (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLog(@"Upload using hashmap finished: %@", objectRequest.url);
     NSString *fileName = [objectRequest.userInfo objectForKey:@"fileName"];
     PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
@@ -1199,10 +1396,12 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
     NSUInteger currentBytes = activity.currentBytes;
     if (objectRequest.responseStatusCode == 201) {
         NSLog(@"Object created: %@", objectRequest.url);
-        [activityFacility endActivity:activity 
-                          withMessage:[NSString stringWithFormat:@"Uploading '%@' (100%%)", fileName] 
-                           totalBytes:totalBytes 
-                         currentBytes:totalBytes];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:activity 
+                              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] 
+                               totalBytes:totalBytes 
+                             currentBytes:totalBytes];
+        });
         for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
             [node forceRefresh];
         }
@@ -1217,14 +1416,17 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
         NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue];
         if (iteration == 0) {
             NSLog(@"Upload iteration limit reached: %@", objectRequest.url);
-            [activityFacility endActivity:activity 
-                              withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)", fileName]]; 
-            NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-            [alert setMessageText:@"Upload Timeout"];
-            [alert setInformativeText:[NSString stringWithFormat:@"Upload iteration limit reached for object with path '%@'", 
-                                       [objectRequest.userInfo objectForKey:@"objectName"]]];
-            [alert addButtonWithTitle:@"OK"];
-            [alert runModal];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility endActivity:activity 
+                                  withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]]; 
+                NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+                [alert setMessageText:@"Upload Timeout"];
+                [alert setInformativeText:[NSString stringWithFormat:@"Upload iteration limit reached for object with path '%@'", 
+                                           [objectRequest.userInfo objectForKey:@"objectName"]]];
+                [alert addButtonWithTitle:@"OK"];
+                [alert runModal];
+            });
+            [pool drain];
             return;
         }
         NSLog(@"object is missing hashes: %@", objectRequest.url);
@@ -1233,10 +1435,12 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
         NSUInteger blockSize = [[objectRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue];
         if (totalBytes >= [missingBlocks count]*blockSize)
             currentBytes = totalBytes - [missingBlocks count]*blockSize;
-        [activityFacility updateActivity:activity 
-                             withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (100*(currentBytes + 0.0)/(totalBytes + 0.0))] 
-                              totalBytes:totalBytes 
-                            currentBytes:currentBytes];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility updateActivity:activity 
+                                 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (totalBytes ? (100*(currentBytes + 0.0)/(totalBytes + 0.0)) : 100)] 
+                                  totalBytes:totalBytes 
+                                currentBytes:currentBytes];
+        });
         NSUInteger missingBlockIndex = [missingBlocks firstIndex];
         __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithContainerName:[objectRequest.userInfo objectForKey:@"containerName"] 
                                                                                                                     blockSize:blockSize 
@@ -1244,50 +1448,30 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                                             missingBlockIndex:missingBlockIndex 
                                                                                                                sharingAccount:[objectRequest.userInfo objectForKey:@"sharingAccount"]];
         newContainerRequest.delegate = self;
-        newContainerRequest.didFinishSelector = @selector(uploadMissingBlockFinished:);
-        newContainerRequest.didFailSelector = @selector(uploadMissingBlockFailed:);
+        newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+        newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
         newContainerRequest.userInfo = objectRequest.userInfo;
         [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:(--iteration)] forKey:@"iteration"];
         [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
         [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:missingBlocks forKey:@"missingBlocks"];
         [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"];
+        [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadMissingBlockFinished:)) forKey:@"didFinishSelector"];
         [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){
             [activityFacility updateActivity:activity 
-                                 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] 
+                                 withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] 
                                   totalBytes:activity.totalBytes 
                                 currentBytes:(activity.currentBytes + size)];
         }];
-        [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
+        [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
     } else {
-        NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
-        if (retries > 0) {
-            ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:objectRequest] autorelease];
-            [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-            [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
-        } else {
-            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                              withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)", fileName]];
-            [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
-        }
-    }
-}
-
-- (void)uploadObjectUsingHashMapFailed:(ASIPithosObjectRequest *)objectRequest {
-    NSLog(@"Upload using hashmap failed: %@", objectRequest.url);
-    NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
-    if (retries > 0) {
-        ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:objectRequest] autorelease];
-        [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-        [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
-    } else {
-        [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)", 
-                                       [objectRequest.userInfo objectForKey:@"fileName"]]];
-        [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
+        [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
+        [self requestFailed:objectRequest];
     }
+    [pool drain];
 }
 
 - (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLog(@"Upload of missing block finished: %@", containerRequest.url);
     NSUInteger blockSize = [[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue];
     NSString *fileName = [containerRequest.userInfo objectForKey:@"fileName"];
@@ -1308,13 +1492,14 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                                          hashes:&hashes 
                                                                                                  sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]];
             newObjectRequest.delegate = self;
-            newObjectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
-            newObjectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
+            newObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+            newObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
             newObjectRequest.userInfo = containerRequest.userInfo;
             [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
             [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlocks"];
             [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlockIndex"];
-            [[PithosUtilities prepareRequest:newObjectRequest] startAsynchronous];
+            [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)) forKey:@"didFinishSelector"];
+            [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]];
         } else {
             __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithContainerName:[containerRequest.userInfo objectForKey:@"containerName"]
                                                                                                                         blockSize:[[containerRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]
@@ -1322,57 +1507,34 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                                                 missingBlockIndex:missingBlockIndex 
                                                                                                                    sharingAccount:[containerRequest.userInfo objectForKey:@"sharingAccount"]];
             newContainerRequest.delegate = self;
-            newContainerRequest.didFinishSelector = @selector(uploadMissingBlockFinished:);
-            newContainerRequest.didFailSelector = @selector(uploadMissingBlockFailed:);
+            newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+            newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
             newContainerRequest.userInfo = containerRequest.userInfo;
             [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
             [(NSMutableDictionary *)(newContainerRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"];
             [newContainerRequest setBytesSentBlock:^(unsigned long long size, unsigned long long total){
                 [activityFacility updateActivity:activity 
-                                     withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] 
+                                     withMessage:[NSString stringWithFormat:@"Uploading '%@' (%.0f%%)", fileName, (activity.totalBytes ? (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0)) : 100)] 
                                       totalBytes:activity.totalBytes 
                                     currentBytes:(activity.currentBytes + size)];
             }];
-            [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
-        }
-    } else {
-        NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
-        if (retries > 0) {
-            ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[[PithosUtilities copyRequest:containerRequest] autorelease];
-            [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-            [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
-        } else {
-            [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
-                              withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)", fileName]];
-            [PithosUtilities unexpectedResponseStatusAlertWithRequest:containerRequest];
+            [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
         }
-    }
-}
-
-- (void)uploadMissingBlockFailed:(ASIPithosContainerRequest *)containerRequest {
-    NSLog(@"Upload of missing block failed: %@", containerRequest.url);
-    NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
-    if (retries > 0) {
-        ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[[PithosUtilities copyRequest:containerRequest] autorelease];
-        [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-        [[PithosUtilities prepareRequest:newContainerRequest] startAsynchronous];
     } else {
-        [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[NSString stringWithFormat:@"Uploading '%@' (failed)", 
-                                       [containerRequest.userInfo objectForKey:@"fileName"]]];
-        [PithosUtilities httpRequestErrorAlertWithRequest:containerRequest];
+        [(NSMutableDictionary *)(containerRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
+        [self requestFailed:containerRequest];
     }
+    [pool drain];
 }
 
 - (void)moveFinished:(ASIPithosObjectRequest *)objectRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLog(@"Move object finished: %@", objectRequest.url);
     if (objectRequest.responseStatusCode == 201) {
-        [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@' (finished)", 
-                                       [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
-                                       [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
-                                       [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
-                                       [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
+                              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+        });
         for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
             [node forceRefresh];
         }
@@ -1384,50 +1546,20 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
         else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
             [self refresh:self];
     } else {
-        NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
-        if (retries > 0) {
-            ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:objectRequest] autorelease];
-            [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-            [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
-        } else {
-            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                              withMessage:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@' (failed)", 
-                                           [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
-                                           [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
-                                           [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
-                                           [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
-            [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
-        }
-    }
-}
-
-- (void)moveFailed:(ASIPithosObjectRequest *)objectRequest {
-    NSLog(@"Move object failed: %@", objectRequest.url);
-    NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
-    if (retries > 0) {
-        ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:objectRequest] autorelease];
-        [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-        [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
-    } else {
-        [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@' (failed)", 
-                                       [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
-                                       [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
-                                       [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
-                                       [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
-        [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
+        [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
+        [self requestFailed:objectRequest];
     }
+    [pool drain];
 }
 
 - (void)copyFinished:(ASIPithosObjectRequest *)objectRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLog(@"Copy object finished: %@", objectRequest.url);
     if (objectRequest.responseStatusCode == 201) {
-        [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@' (finished)", 
-                                       [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
-                                       [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
-                                       [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
-                                       [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
+                              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+        });
         for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
             [node forceRefresh];
         }
@@ -1439,47 +1571,20 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
         else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
             [self refresh:self];
     } else {
-        NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
-        if (retries > 0) {
-            ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:objectRequest] autorelease];
-            [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-            [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
-        } else {
-            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                              withMessage:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@' (failed)", 
-                                           [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
-                                           [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
-                                           [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
-                                           [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
-            [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
-        }
-    }
-}
-
-- (void)copyFailed:(ASIPithosObjectRequest *)objectRequest {
-    NSLog(@"Copy object failed: %@", objectRequest.url);
-    NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
-    if (retries > 0) {
-        ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:objectRequest] autorelease];
-        [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-        [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
-    } else {
-        [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[NSString stringWithFormat:@"Copying '%@/%@' to '%@/%@' (failed)", 
-                                       [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
-                                       [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
-                                       [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
-                                       [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
-        [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
+        [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
+        [self requestFailed:objectRequest];
     }
+    [pool drain];
 }
 
 - (void)deleteObjectFinished:(ASIPithosObjectRequest *)objectRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLog(@"Delete object finished: %@", objectRequest.url);
     if (objectRequest.responseStatusCode == 204) {
-        [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[NSString stringWithFormat:@"Deleting '%@' (finished)", 
-                                       [objectRequest.userInfo objectForKey:@"fileName"]]];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
+                              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+        });
         for (PithosNode *node in [objectRequest.userInfo objectForKey:@"forceRefreshNodes"]) {
             [node forceRefresh];
         }
@@ -1491,33 +1596,10 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
         else if ([[objectRequest.userInfo objectForKey:@"refresh"] boolValue])
             [self refresh:self];
     } else {
-        NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
-        if (retries > 0) {
-            ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:objectRequest] autorelease];
-            [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-            [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
-        } else {
-            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                              withMessage:[NSString stringWithFormat:@"Deleting '%@' (failed)", 
-                                           [objectRequest.userInfo objectForKey:@"fileName"]]];
-            [PithosUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
-        }
-    }
-}
-
-- (void)deleteObjectFailed:(ASIPithosObjectRequest *)objectRequest {
-    NSLog(@"Delete object failed: %@", objectRequest.url);
-    NSUInteger retries = [[objectRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
-    if (retries > 0) {
-        ASIPithosObjectRequest *newObjectRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:objectRequest] autorelease];
-        [(NSMutableDictionary *)(newObjectRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-        [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
-    } else {
-        [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[NSString stringWithFormat:@"Deleting '%@' (failed)", 
-                                       [objectRequest.userInfo objectForKey:@"fileName"]]];
-        [PithosUtilities httpRequestErrorAlertWithRequest:objectRequest];
+        [(NSMutableDictionary *)(objectRequest.userInfo)setObject:[NSNumber numberWithBool:YES] forKey:@"unexpectedResponseStatus"];
+        [self requestFailed:objectRequest];
     }
+    [pool drain];
 }
 
 #pragma mark -
@@ -1823,8 +1905,8 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 - (void)menuNewFolder:(NSMenuItem *)sender {
     PithosNode *node = (PithosNode *)[sender representedObject];
     if ([node class] == [PithosContainerNode class]) {
-        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-        dispatch_async(queue, ^{
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
             NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:node.pithosContainer.name 
                                                                                 subdirName:@"untitled folder"];
             NSString *fileName = [safeObjectName lastPathComponent];
@@ -1840,23 +1922,33 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                                        metadata:nil 
                                                                                                            data:[NSData data]];
             objectRequest.delegate = self;
-            objectRequest.didFinishSelector = @selector(uploadDirectoryObjectFinished:);
-            objectRequest.didFailSelector = @selector(uploadDirectoryObjectFailed:);
+            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+            NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", fileName];
             PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory 
-                                                                       message:[NSString stringWithFormat:@"Creating directory '%@'", fileName]];
+                                                                       message:messagePrefix];
             objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                       fileName, @"fileName", 
                                       [NSArray arrayWithObject:node], @"refreshNodes", 
                                       [NSNumber numberWithBool:YES], @"refresh", 
                                       activity, @"activity", 
+                                      [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                                      [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                                      [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
+                                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                                       [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                                      NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
+                                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                                      uploadNetworkQueue, @"networkQueue", 
+                                      @"uploadQueue", @"queueType", 
                                       nil];
-            [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+            [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
+            [pool drain];
         });
     } else if (([node class] == [PithosSubdirNode class]) && 
                (node.pithosObject.subdir || ![node.pithosObject.name hasSuffix:@"/"])) {
-        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-        dispatch_async(queue, ^{
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
             NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:node.pithosContainer.name 
                                                                                subdirName:[node.pithosObject.name stringByAppendingPathComponent:@"untitled folder"]];
             NSString *fileName = [safeObjectName lastPathComponent];
@@ -1872,18 +1964,28 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                                           metadata:nil 
                                                                                                               data:[NSData data]];
             objectRequest.delegate = self;
-            objectRequest.didFinishSelector = @selector(uploadDirectoryObjectFinished:);
-            objectRequest.didFailSelector = @selector(uploadDirectoryObjectFailed:);
+            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+            NSString *messagePrefix = [NSString stringWithFormat:@"Creating directory '%@'", fileName];
             PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory 
-                                                                       message:[NSString stringWithFormat:@"Creating directory '%@'", fileName]];
+                                                                       message:messagePrefix];
             objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                       fileName, @"fileName", 
                                       [NSArray arrayWithObject:node], @"refreshNodes", 
                                       [NSNumber numberWithBool:YES], @"refresh", 
                                       activity, @"activity", 
+                                      [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                                      [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                                      [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
+                                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                                       [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                                      NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
+                                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                                      uploadNetworkQueue, @"networkQueue", 
+                                      @"uploadQueue", @"queueType", 
                                       nil];
-            [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+            [uploadNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
+            [pool drain];
         });
     }
 }
@@ -1898,45 +2000,67 @@ 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:@"/"])) {
-            NSString *fileName = [node.pithosObject.name lastPathComponent];
-            if ([node.pithosObject.name hasSuffix:@"/"])
-                fileName = [fileName stringByAppendingString:@"/"];
-            ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithContainerName:node.pithosContainer.name 
-                                                                                                      objectName:node.pithosObject.name];
-            objectRequest.delegate = self;
-            objectRequest.didFinishSelector = @selector(deleteObjectFinished:);
-            objectRequest.didFailSelector = @selector(deleteObjectFailed:);
-            PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete 
-                                                                       message:[NSString stringWithFormat:@"Deleting '%@'", fileName]];
-            objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
-                                      fileName, @"fileName", 
-                                      [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", 
-                                      activity, @"activity", 
-                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
-                                      nil];
-            [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+                NSString *fileName = [node.pithosObject.name lastPathComponent];
+                if ([node.pithosObject.name hasSuffix:@"/"])
+                    fileName = [fileName stringByAppendingString:@"/"];
+                ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithContainerName:node.pithosContainer.name 
+                                                                                                          objectName:node.pithosObject.name];
+                objectRequest.delegate = self;
+                objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                NSString *messagePrefix = [NSString stringWithFormat:@"Deleting '%@'", fileName];
+                PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete 
+                                                                           message:messagePrefix];
+                objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                          fileName, @"fileName", 
+                                          [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", 
+                                          activity, @"activity", 
+                                          [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                                          [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                                          [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
+                                          [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
+                                          [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                                          NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", 
+                                          NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                                          deleteNetworkQueue, @"networkQueue", 
+                                          @"deleteQueue", @"queueType", 
+                                          nil];
+                [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
+                [pool drain];
+            });
         } else if ([node class] == [PithosSubdirNode class]) {
-            dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-            dispatch_async(queue, ^{
+            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                 NSArray *objectRequests = [PithosUtilities deleteObjectRequestsForSubdirWithContainerName:node.pithosContainer.name 
                                                                                                objectName:node.pithosObject.name];
                 if (objectRequests) {
                     for (ASIPithosObjectRequest *objectRequest in objectRequests) {
                         objectRequest.delegate = self;
-                        objectRequest.didFinishSelector = @selector(deleteObjectFinished:);
-                        objectRequest.didFailSelector = @selector(deleteObjectFailed:);
+                        objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                        objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                        NSString *messagePrefix = [NSString stringWithFormat:@"Deleting '%@'", [objectRequest.userInfo objectForKey:@"fileName"]];
                         PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete 
-                                                                                   message:[NSString stringWithFormat:@"Deleting '%@'", 
-                                                                                            [objectRequest.userInfo objectForKey:@"fileName"]]];
+                                                                                   message:messagePrefix];
                         [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
                          [NSDictionary dictionaryWithObjectsAndKeys:
                           [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", 
                           activity, @"activity", 
+                          [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                          [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                          [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
+                          [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                           [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                          NSStringFromSelector(@selector(deleteObjectFinished:)), @"didFinishSelector", 
+                          NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                          deleteNetworkQueue, @"networkQueue", 
+                          @"deleteQueue", @"queueType", 
                           nil]];
-                        [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+                        [deleteNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                     }
                 }
+                [pool drain];
             });
         }
     }
@@ -1948,8 +2072,8 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
             (([node class] == [PithosSubdirNode class]) && 
              !node.pithosObject.subdir &&
              [node.pithosObject.name hasSuffix:@"/"])) {
-            dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-            dispatch_async(queue, ^{
+            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                 NSString *safeObjectName = [PithosUtilities safeObjectNameForContainerName:@"trash" 
                                                                                     objectName:node.pithosObject.name];
                 if (safeObjectName) {
@@ -1960,27 +2084,37 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                                       checkIfExists:NO];
                     if (objectRequest) {
                         objectRequest.delegate = self;
-                        objectRequest.didFinishSelector = @selector(moveFinished:);
-                        objectRequest.didFailSelector = @selector(moveFailed:);
+                        objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                        objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                        NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", 
+                                                   [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
+                                                   [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
+                                                   [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
+                                                   [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
                         PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove 
-                                                                                   message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", 
-                                                                                            [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
-                                                                                            [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
-                                                                                            [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
-                                                                                            [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
+                                                                                   message:messagePrefix];
                         [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
                          [NSDictionary dictionaryWithObjectsAndKeys:
                           [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", 
                           activity, @"activity", 
+                          [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                          [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                          [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
+                          [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                           [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                          NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
+                          NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                          moveNetworkQueue, @"networkQueue", 
+                          @"moveQueue", @"queueType", 
                           nil]];
-                        [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+                        [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                     }
                 }
+                [pool drain];
             });
         } else if ([node class] == [PithosSubdirNode class]) {
-            dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-            dispatch_async(queue, ^{
+            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                 NSString *safeObjectName = [PithosUtilities safeSubdirNameForContainerName:@"trash" 
                                                                                     subdirName:node.pithosObject.name];
                 if (safeObjectName) {
@@ -1992,24 +2126,34 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                     if (objectRequests) {
                         for (ASIPithosObjectRequest *objectRequest in objectRequests) {
                             objectRequest.delegate = self;
-                            objectRequest.didFinishSelector = @selector(moveFinished:);
-                            objectRequest.didFailSelector = @selector(moveFailed:);
+                            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
+                            NSString *messagePrefix = [NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", 
+                                                       [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
+                                                       [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
+                                                       [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
+                                                       [objectRequest.userInfo objectForKey:@"destinationObjectName"]];
                             PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityMove 
-                                                                                       message:[NSString stringWithFormat:@"Moving '%@/%@' to '%@/%@'", 
-                                                                                                [objectRequest.userInfo objectForKey:@"sourceContainerName"], 
-                                                                                                [objectRequest.userInfo objectForKey:@"sourceObjectName"], 
-                                                                                                [objectRequest.userInfo objectForKey:@"destinationContainerName"], 
-                                                                                                [objectRequest.userInfo objectForKey:@"destinationObjectName"]]];
+                                                                                       message:messagePrefix];
                             [(NSMutableDictionary *)(objectRequest.userInfo) addEntriesFromDictionary:
                              [NSDictionary dictionaryWithObjectsAndKeys:
                               [NSArray arrayWithObject:node.parent], @"forceRefreshNodes", 
                               activity, @"activity", 
+                              [messagePrefix stringByAppendingString:@" (stopped)"], @"stoppedActivityMessage", 
+                              [messagePrefix stringByAppendingString:@" (failed)"], @"failedActivityMessage", 
+                              [messagePrefix stringByAppendingString:@" (finished)"], @"finishedActivityMessage", 
+                              [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                               [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                              NSStringFromSelector(@selector(moveFinished:)), @"didFinishSelector", 
+                              NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
+                              moveNetworkQueue, @"networkQueue", 
+                              @"moveQueue", @"queueType", 
                               nil]];
-                            [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
+                            [moveNetworkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
                         }
                     }
                 }
+                [pool drain];
             });
         }
     }