Memory leaks plugged.
[pithos-macos] / pithos-macos / PithosSyncDaemon.m
index fec9018..f80e522 100644 (file)
             self.storedLocalObjectStates = [NSMutableDictionary dictionary];
         }
         
-        queue = [[ASINetworkQueue alloc] init];
-        queue.showAccurateProgress = YES;
-        queue.shouldCancelAllRequestsOnFailure = NO;
-        [queue go];
+        networkQueue = [[ASINetworkQueue alloc] init];
+        networkQueue.showAccurateProgress = YES;
+        networkQueue.shouldCancelAllRequestsOnFailure = NO;
+        [networkQueue go];
         
         [[NSNotificationCenter defaultCenter] addObserver:self
                                                  selector:@selector(applicationWillTerminate:)
 
 - (void)dealloc {
     [[NSNotificationCenter defaultCenter] removeObserver:self];
-    [queue cancelAllOperations];
-    [queue release];
+    [networkQueue cancelAllOperations];
+    [networkQueue release];
     [timer invalidate];
     [timer release];
     [tempTrashDirPath release];
 }
 
 #pragma mark -
+#pragma mark Background
+
+- (void)fileActionFailedAlert:(NSDictionary *)args {
+    [PithosUtilities fileActionFailedAlertWithTitle:[args objectForKey:@"title"] 
+                                            message:[args objectForKey:@"message"] 
+                                              error:[args objectForKey:@"error"]];
+}
+
+- (void)fileActionFailedAlertWithTitle:(NSString *)title 
+                               message:(NSString *)message 
+                                 error:(NSError *)error {
+    NSMutableDictionary *args = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                 title, @"title", 
+                                 message, @"message", 
+                                 nil];
+    if (error)
+        [args setObject:error forKey:@"error"];
+    [self performSelectorOnMainThread:@selector(fileActionFailedAlert:) 
+                           withObject:args
+                        waitUntilDone:YES];
+}
+
+- (void)startAndEndActivity:(NSDictionary *)args {
+    [activityFacility startAndEndActivityWithType:[[args objectForKey:@"type"] intValue] message:[args objectForKey:@"message"]];
+}
+
+- (void)startAndEndActivityWithType:(PithosActivityType)type message:(NSString *)message {
+    NSDictionary *args = [NSDictionary dictionaryWithObjectsAndKeys:
+                          [NSNumber numberWithInt:type], @"type", 
+                          message, @"message", 
+                          nil];
+    [self performSelectorOnMainThread:@selector(startAndEndActivity:) 
+                           withObject:args
+                        waitUntilDone:YES];
+}
+
+- (void)updateActivity:(NSDictionary *)args {
+    NSNumber *totalBytesNumber = [args objectForKey:@"totalBytes"];
+    NSNumber *currentBytesNumber = [args objectForKey:@"currentBytes"];
+    if (totalBytesNumber && currentBytesNumber)
+        [activityFacility updateActivity:[args objectForKey:@"activity"] 
+                             withMessage:[args objectForKey:@"message"]
+                              totalBytes:[totalBytesNumber unsignedIntegerValue] 
+                            currentBytes:[currentBytesNumber unsignedIntegerValue]];
+    else
+        [activityFacility updateActivity:[args objectForKey:@"activity"] 
+                             withMessage:[args objectForKey:@"message"]];
+}
+
+- (void)updateActivity:(PithosActivity *)activity 
+           withMessage:(NSString *)message 
+            totalBytes:(NSUInteger)totalBytes 
+          currentBytes:(NSUInteger)currentBytes {
+    NSMutableDictionary *args = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                 activity, @"activity", 
+                                 [NSNumber numberWithUnsignedInteger:totalBytes], @"totalBytes", 
+                                 [NSNumber numberWithUnsignedInteger:currentBytes], @"currentBytes", 
+                                 nil];
+    if (message)
+        [args setObject:message forKey:@"message"];
+    [self performSelectorOnMainThread:@selector(updateActivity:) 
+                           withObject:args
+                        waitUntilDone:YES];
+}
+
+- (void)updateActivity:(PithosActivity *)activity withMessage:(NSString *)message {
+    NSMutableDictionary *args = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                 activity, @"activity", 
+                                 nil];
+    if (message)
+        [args setObject:message forKey:@"message"];
+    [self performSelectorOnMainThread:@selector(updateActivity:) 
+                           withObject:args
+                        waitUntilDone:YES];
+}
+
+- (void)endActivity:(NSDictionary *)args {
+    NSNumber *totalBytesNumber = [args objectForKey:@"totalBytes"];
+    NSNumber *currentBytesNumber = [args objectForKey:@"currentBytes"];
+    if (totalBytesNumber && currentBytesNumber)
+        [activityFacility endActivity:[args objectForKey:@"activity"] 
+                          withMessage:[args objectForKey:@"message"]
+                           totalBytes:[totalBytesNumber unsignedIntegerValue] 
+                         currentBytes:[currentBytesNumber unsignedIntegerValue]];
+    else
+        [activityFacility endActivity:[args objectForKey:@"activity"] 
+                          withMessage:[args objectForKey:@"message"]];
+}
+
+- (void)endActivity:(PithosActivity *)activity
+        withMessage:(NSString *)message 
+         totalBytes:(NSUInteger)totalBytes 
+       currentBytes:(NSUInteger)currentBytes {
+    NSMutableDictionary *args = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                 activity, @"activity", 
+                                 [NSNumber numberWithUnsignedInteger:totalBytes], @"totalBytes", 
+                                 [NSNumber numberWithUnsignedInteger:currentBytes], @"currentBytes", 
+                                 nil];
+    if (message)
+        [args setObject:message forKey:@"message"];
+    [self performSelectorOnMainThread:@selector(endActivity:) 
+                           withObject:args
+                        waitUntilDone:YES];
+}
+
+- (void)endActivity:(PithosActivity *)activity withMessage:(NSString *)message {
+    NSMutableDictionary *args = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                 activity, @"activity", 
+                                 nil];
+    if (message)
+        [args setObject:message forKey:@"message"];
+    [self performSelectorOnMainThread:@selector(endActivity:) 
+                           withObject:args
+                        waitUntilDone:YES];
+}
+
+#pragma mark -
 #pragma mark Observers
 
 - (void)applicationWillTerminate:(NSNotification *)notification {
 - (NSString *)pithosStateFilePath {
     if (!pithosStateFilePath)
         pithosStateFilePath = [[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"PithosLocalObjectStates.archive"] retain];
-    return [pithosStateFilePath copy];
+    return [[pithosStateFilePath copy] autorelease];
 }
 
 - (NSString *)tempDownloadsDirPath {
             [userDefaults setObject:tempDownloadsDirPath forKey:@"PithosSyncTempDownloadsDirPath"];
         [tempDownloadsDirPath retain];
     }
-    return [tempDownloadsDirPath copy];
+    return [[tempDownloadsDirPath copy] autorelease];
 }
 
 - (NSString *)tempTrashDirPath {
             [userDefaults setObject:tempTrashDirPath forKey:@"PithosSyncTempTrashDirPath"];
         [tempTrashDirPath retain];
     }
-    return [tempTrashDirPath copy];
+    return [[tempTrashDirPath copy] autorelease];
 }
 
 #pragma mark -
         if (!syncOperationCount) {
             if (!syncIncomplete) {
                 self.lastCompletedSync = [NSDate date];
-                [activityFacility startAndEndActivityWithType:PithosActivityOther 
-                                                      message:[NSString stringWithFormat:@"Sync: Completed %@", lastCompletedSync]];
+                [self startAndEndActivityWithType:PithosActivityOther 
+                                          message:[NSString stringWithFormat:@"Sync: Completed %@", lastCompletedSync]];
             }
             [self emptyTempTrash];
         }
                                                                                                            until:nil 
                                                                                                  ifModifiedSince:lastModified];
     containerRequest.delegate = self;
-    containerRequest.didFinishSelector = @selector(listRequestFinished:);
-    containerRequest.didFailSelector = @selector(listRequestFailed:);
+    containerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+    containerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
     PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityOther 
                                                                message:@"Sync: Getting server listing"];
     containerRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                  @"Sync: Getting server listing (finished)", @"finishedActivityMessage", 
                                  [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", 
                                  [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                                 NSStringFromSelector(@selector(listRequestFinished:)), @"didFinishSelector", 
+                                 NSStringFromSelector(@selector(listRequestFailed:)), @"didFailSelector", 
                                  nil];
-    [queue addOperation:[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh]];
+    [networkQueue addOperation:[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh]];
 }
 
 - (void)emptyTempTrash {
 //        NSArray *subPaths = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:trashDirPath error:&error];
         NSArray *subPaths = [fileManager contentsOfDirectoryAtPath:trashDirPath error:&error];
         if (error) {
-            [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" 
-                                                    message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", containerDirectoryPath] 
-                                                      error:error];
+            [self fileActionFailedAlertWithTitle:@"Directory Contents Error" 
+                                         message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", containerDirectoryPath] 
+                                           error:error];
             return;
         }
         if ([subPaths count]) {
 //            syncOperationCount = 1;
 //            [[NSWorkspace sharedWorkspace] recycleURLs:subURLs completionHandler:^(NSDictionary *newURLs, NSError *error) {
 //                if (error) {
-//                    [PithosUtilities fileActionFailedAlertWithTitle:@"Move to Trash Error" 
-//                                                            message:@"Cannot move files to Trash" 
-//                                                              error:error];
+//                    [self fileActionFailedAlertWithTitle:@"Move to Trash Error" 
+//                                                 message:@"Cannot move files to Trash" 
+//                                                   error:error];
 //                }
 //                syncOperationCount = 0;
 //            }];
                 NSString *subFilePath = [trashDirPath stringByAppendingPathComponent:subPath];
                 error = nil;
                 if (![fileManager removeItemAtPath:subFilePath error:&error] || error)
-                    [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error" 
-                                                            message:[NSString stringWithFormat:@"Cannot remove file at '%@'", subFilePath] 
-                                                              error:error];
+                    [self fileActionFailedAlertWithTitle:@"Remove File Error" 
+                                                 message:[NSString stringWithFormat:@"Cannot remove file at '%@'", subFilePath] 
+                                                   error:error];
             }
         }
     }    
     if (fileExists && isDirectory) {
         NSArray *subPaths = [fileManager subpathsOfDirectoryAtPath:filePath error:&error];
         if (error) {
-            [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" 
-                                                    message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", containerDirectoryPath] 
-                                                      error:error];
+            [self fileActionFailedAlertWithTitle:@"Directory Contents Error" 
+                                         message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", containerDirectoryPath] 
+                                           error:error];
             return NO;
         }
         if (![fileManager createDirectoryAtPath:newDirPath withIntermediateDirectories:YES attributes:nil error:&error] || error) {
-            [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" 
-                                                    message:[NSString stringWithFormat:@"Cannot create directory at '%@'", newDirPath] 
-                                                      error:error];
+            [self fileActionFailedAlertWithTitle:@"Create Directory Error" 
+                                         message:[NSString stringWithFormat:@"Cannot create directory at '%@'", newDirPath] 
+                                           error:error];
             return NO;
         }
         if (![fileManager moveItemAtPath:filePath toPath:newFilePath error:&error] || error) {
-            [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" 
-                                                    message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", 
-                                                             filePath, newFilePath] 
-                                                      error:error];
+            [self fileActionFailedAlertWithTitle:@"Move File Error" 
+                                         message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", 
+                                                  filePath, newFilePath] 
+                                           error:error];
             return NO;
         }
         PithosLocalObjectState *currentState = [currentLocalObjectStates objectForKey:filePath];
         }
     } else if (fileExists && !isDirectory) {
         if (![fileManager createDirectoryAtPath:newDirPath withIntermediateDirectories:YES attributes:nil error:&error] || error) {
-            [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" 
-                                                    message:[NSString stringWithFormat:@"Cannot create directory at '%@'", newDirPath] 
-                                                      error:error];
+            [self fileActionFailedAlertWithTitle:@"Create Directory Error" 
+                                         message:[NSString stringWithFormat:@"Cannot create directory at '%@'", newDirPath] 
+                                           error:error];
             return NO;
         }
         if (![fileManager moveItemAtPath:filePath toPath:newFilePath error:&error] || error) {
-            [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" 
-                                                    message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", 
-                                                             filePath, newFilePath] 
-                                                      error:error];
+            [self fileActionFailedAlertWithTitle:@"Move File Error" 
+                                         message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", 
+                                                  filePath, newFilePath] 
+                                           error:error];
             return NO;
         }
         PithosLocalObjectState *currentState = [currentLocalObjectStates objectForKey:filePath];
             [fileManager fileExistsAtPath:localFilePath isDirectory:&isDirectory] && !isDirectory) {
             if ([localFilePath hasPrefix:containerDirectoryPath]) {
                 if (![fileManager copyItemAtPath:localFilePath toPath:filePath error:&error] || error) {
-                    [PithosUtilities fileActionFailedAlertWithTitle:@"Copy File Error" 
-                                                            message:[NSString stringWithFormat:@"Cannot copy file at '%@' to '%@'", 
-                                                                     localFilePath, filePath] 
-                                                              error:error];
+                    [self fileActionFailedAlertWithTitle:@"Copy File Error" 
+                                                 message:[NSString stringWithFormat:@"Cannot copy file at '%@' to '%@'", 
+                                                          localFilePath, filePath] 
+                                                   error:error];
                 } else {
                     return YES;
                 }            
             } else if (self.tempTrashDirPath && [localFilePath hasPrefix:self.tempTrashDirPath]) {
                 if (![fileManager moveItemAtPath:localFilePath toPath:filePath error:&error] || error) {
-                    [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" 
-                                                            message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", 
-                                                                     localFilePath, filePath] 
-                                                              error:error];
+                    [self fileActionFailedAlertWithTitle:@"Move File Error" 
+                                                 message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", 
+                                                          localFilePath, filePath] 
+                                                   error:error];
                 } else {
                     localState.filePath = filePath;
                     [currentLocalObjectStates setObject:localState forKey:filePath];
         // Delete local object
         NSLog(@"Sync::delete local object: %@", filePath);
         if (!fileExists || [self moveToTempTrashFile:filePath]) {
-            [activityFacility startAndEndActivityWithType:PithosActivityOther 
-                                                  message:[NSString stringWithFormat:@"Sync: Deleting '%@' locally (finished)", object.name]];
+            [self startAndEndActivityWithType:PithosActivityOther 
+                                      message:[NSString stringWithFormat:@"Sync: Deleting '%@' locally (finished)", object.name]];
             [storedLocalObjectStates removeObjectForKey:object.name];
             [self saveLocalState];
         }
             NSLog(@"Sync::local directory object doesn't exist: %@", filePath);
             error = nil;
             if (![fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:&error] || error) {
-                [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" 
-                                                        message:[NSString stringWithFormat:@"Cannot create directory at '%@'", filePath] 
-                                                          error:error];
+                [self fileActionFailedAlertWithTitle:@"Create Directory Error" 
+                                             message:[NSString stringWithFormat:@"Cannot create directory at '%@'", filePath] 
+                                               error:error];
             } else {
                 directoryCreated = YES;
                 storedState.isDirectory = YES;
             directoryCreated = YES;
         }
         if (directoryCreated)
-            [activityFacility startAndEndActivityWithType:PithosActivityOther 
-                                                  message:[NSString stringWithFormat:@"Sync: Creating directory '%@' locally (finished)", object.name]];
+            [self startAndEndActivityWithType:PithosActivityOther 
+                                      message:[NSString stringWithFormat:@"Sync: Creating directory '%@' locally (finished)", object.name]];
     } else if (object.bytes == 0) {
         // Create local object with zero length
         NSLog(@"Sync::create local zero length object: %@", filePath);
             NSLog(@"Sync::local zero length object doesn't exist: %@", filePath);
             error = nil;
             if (![fileManager createFileAtPath:filePath contents:nil attributes:nil]) {
-                [PithosUtilities fileActionFailedAlertWithTitle:@"Create File Error" 
-                                                        message:[NSString stringWithFormat:@"Cannot create file at '%@'", filePath] 
-                                                          error:error];
+                [self fileActionFailedAlertWithTitle:@"Create File Error" 
+                                             message:[NSString stringWithFormat:@"Cannot create file at '%@'", filePath] 
+                                               error:error];
             } else {
                 fileCreated = YES;
                 storedState.hash = object.hash;
             fileCreated = YES;
         }
         if (fileCreated)
-            [activityFacility startAndEndActivityWithType:PithosActivityOther 
-                                                  message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
+            [self startAndEndActivityWithType:PithosActivityOther 
+                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
     } else if (storedState.filePath == nil) {
         // Create new local object
         // Check first if a local copy exists
         if ([self findLocalCopyForObjectWithHash:object.hash forFile:filePath]) {
-            [activityFacility startAndEndActivityWithType:PithosActivityOther 
-                                                  message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
+            [self startAndEndActivityWithType:PithosActivityOther 
+                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
         } else {
             [self increaseSyncOperationCount];
             __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectBlockDataRequestWithContainerName:containerName 
                                                                                                           blockIndex:0 
                                                                                                            blockSize:blockSize];
             objectRequest.delegate = self;
-            objectRequest.didFinishSelector = @selector(downloadObjectBlockFinished:);
-            objectRequest.didFailSelector = @selector(requestFailed:);
+            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
             PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload 
                                                                        message:[NSString stringWithFormat:@"Sync: Downloading '%@' (0%%)", object.name] 
                                                                     totalBytes:object.bytes 
                                                                   currentBytes:0];
+            [self updateActivity:activity withMessage:activity.message];
             objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                       object, @"pithosObject", 
                                       [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, (NSUInteger)ceil((object.bytes +0.0)/(blockSize + 0.0)))], @"missingBlocks", 
                                       [NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name], @"finishedActivityMessage", 
                                       [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
                                       [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                                      NSStringFromSelector(@selector(downloadObjectBlockFinished:)), @"didFinishSelector", 
+                                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                                       nil];
             [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
                 [activityFacility updateActivity:activity 
                                       totalBytes:activity.totalBytes 
                                     currentBytes:(activity.currentBytes + size)];
             }];
-            [queue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]];
+            [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]];
         }
     } else {
         // Resume local object download
         // Check first if a local copy exists
         if ([self findLocalCopyForObjectWithHash:object.hash forFile:filePath]) {
-            [activityFacility startAndEndActivityWithType:PithosActivityOther 
-                                                  message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
+            [self startAndEndActivityWithType:PithosActivityOther 
+                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
             // Delete incomplete temp download
             error = nil;
             if (![fileManager removeItemAtPath:storedState.filePath error:&error] || error) {
-                [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error" 
-                                                        message:[NSString stringWithFormat:@"Cannot remove file at '%@'", storedState.filePath] 
-                                                          error:error];
+                [self fileActionFailedAlertWithTitle:@"Remove File Error" 
+                                             message:[NSString stringWithFormat:@"Cannot remove file at '%@'", storedState.filePath] 
+                                               error:error];
             }
         } else {
             [self increaseSyncOperationCount];
             ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectHashmapRequestWithContainerName:containerName 
                                                                                                        objectName:object.name];
             objectRequest.delegate = self;
-            objectRequest.didFinishSelector = @selector(downloadObjectHashMapFinished:);
-            // The fail method for block download does exactly what we want
-            objectRequest.didFailSelector = @selector(requestFailed:);
+            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
             PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload 
                                                                        message:[NSString stringWithFormat:@"Sync: Downloading '%@' (0%%)", object.name] 
                                                                     totalBytes:object.bytes 
                                                                   currentBytes:0];
+            [self updateActivity:activity withMessage:activity.message];
             objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                       object, @"pithosObject", 
                                       filePath, @"filePath", 
                                       [NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name], @"finishedActivityMessage", 
                                       [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
                                       [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                                      NSStringFromSelector(@selector(downloadObjectHashMapFinished:)), @"didFinishSelector", 
+                                      NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                                       nil];
-            [queue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]];
+            [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]];
         }
     }
 }
                                   object:(ASIPithosObject *)object 
                            localFilePath:(NSString *)filePath {
     [self increaseSyncOperationCount];
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    BOOL isDirectory;
+    BOOL fileExists = [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory];
     if (currentState.isDirectory) {
         // Create remote directory object
+        if (!fileExists || !isDirectory) {
+            // Local directory object deleted or changed to a file in the meantime, mark the sync cycle as incomplete and skip
+            syncIncomplete = YES;
+            [self decreaseSyncOperationCount];
+            return;
+        }
         ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName 
                                                                                                      objectName:object.name 
                                                                                                            eTag:nil 
                                                                                                        metadata:nil 
                                                                                                            data:[NSData data]];
         objectRequest.delegate = self;
-        objectRequest.didFinishSelector = @selector(uploadDirectoryObjectFinished:);
-        objectRequest.didFailSelector = @selector(requestFailed:);
+        objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+        objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
         PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory 
                                                                    message:[NSString stringWithFormat:@"Sync: Creating directory '%@'", object.name]];
+        [self updateActivity:activity withMessage:activity.message];
         objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                   object, @"pithosObject", 
                                   activity, @"activity", 
                                   [NSString stringWithFormat:@"Sync: Creating directory '%@' (finished)", object.name], @"finishedActivityMessage", 
                                   [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                                   [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                                  NSStringFromSelector(@selector(uploadDirectoryObjectFinished:)), @"didFinishSelector", 
+                                  NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                                   nil];
-        [queue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
+        [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
     } else if (!currentState.exists) {
         // Delete remote object
+        if (fileExists) {
+            // Local object created in the meantime, just mark the sync cycle as incomplete, but do delete the server object
+            syncIncomplete = YES;
+        }
         NSString *safeObjectName = [PithosUtilities safeObjectNameForContainerName:@"trash" 
                                                                         objectName:object.name];
         if (safeObjectName) {
                                                                                           checkIfExists:NO];
             if (objectRequest) {
                 objectRequest.delegate = self;
-                objectRequest.didFinishSelector = @selector(moveObjectToTrashFinished:);
-                objectRequest.didFailSelector = @selector(requestFailed:);
+                objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
                 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete 
                                                                            message:[NSString stringWithFormat:@"Sync: Moving to trash '%@'", object.name]];
+                [self updateActivity:activity withMessage:activity.message];
                 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                           object, @"pithosObject", 
                                           activity, @"activity", 
                                           [NSString stringWithFormat:@"Sync: Moving to trash '%@' (finished)", object.name], @"finishedActivityMessage", 
                                           [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                                           [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                                          NSStringFromSelector(@selector(moveObjectToTrashFinished:)), @"didFinishSelector", 
+                                          NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
                                           nil];
-                [queue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
+                [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
             } else {
                 syncIncomplete = YES;
                 [self decreaseSyncOperationCount];
         }
     } else {
         // Upload file to remote object
+        if (!fileExists || isDirectory) {
+            // Local file object deleted or changed to a directory in the meantime, mark the sync cycle as incomplete and skip
+            syncIncomplete = YES;
+            [self decreaseSyncOperationCount];
+            return;
+        }
         NSError *error = nil;
         object.contentType = [PithosUtilities contentTypeOfFile:filePath error:&error];
         if (object.contentType == nil)
                                                                                           sharingAccount:nil];
         if (objectRequest) {
             objectRequest.delegate = self;
-            objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
-            objectRequest.didFailSelector = @selector(requestFailed:);
+            objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+            objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
             PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload 
                                                                        message:[NSString stringWithFormat:@"Sync: Uploading '%@' (0%%)", object.name]
                                                                     totalBytes:[[objectRequest.userInfo valueForKey:@"bytes"] unsignedIntegerValue]
                                                                   currentBytes:0];
+            [self updateActivity:activity withMessage:activity.message];
             [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
              [NSDictionary dictionaryWithObjectsAndKeys:
               object, @"pithosObject", 
               [NSString stringWithFormat:@"Sync: Uploading '%@' (100%%)", object.name], @"finishedActivityMessage", 
               [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
               [NSNumber numberWithUnsignedInteger:10], @"retries", 
+              NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)), @"didFinishSelector", 
+              NSStringFromSelector(@selector(requestFailed:)), @"didFailSelector", 
               nil]];
-            [queue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]];
+            [networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]];
         } else {
             syncIncomplete = YES;
             [self decreaseSyncOperationCount];
 #pragma mark -
 #pragma mark ASIHTTPRequestDelegate
 
+- (void)performRequestFinishedDelegateInBackground:(ASIPithosRequest *)request {
+    [self performSelectorInBackground:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) withObject:request];
+}
+
+- (void)performRequestFailedDelegateInBackground:(ASIPithosRequest *)request {
+    [self performSelectorInBackground:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) withObject:request];
+}
+
 - (void)listRequestFinished:(ASIPithosContainerRequest *)containerRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLog(@"Sync::list request finished: %@", containerRequest.url);
     if ((containerRequest.responseStatusCode == 200) || (containerRequest.responseStatusCode == 304)) {
         if (containerRequest.responseStatusCode == 200) {
                                                                                                                           until:nil 
                                                                                                                 ifModifiedSince:lastModified];
                 newContainerRequest.delegate = self;
-                newContainerRequest.didFinishSelector = @selector(listRequestFinished:);
-                newContainerRequest.didFailSelector = @selector(listRequestFailed:);
+                newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
                 newContainerRequest.userInfo = newContainerRequest.userInfo;
                 [(NSMutableDictionary *)newContainerRequest.userInfo setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
-                [queue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
+                [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
                 return;
             }
         }
-        [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[containerRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+        [self endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
+              withMessage:[containerRequest.userInfo objectForKey:@"finishedActivityMessage"]];
         NSFileManager *fileManager = [NSFileManager defaultManager];
         NSError *error = nil;
         NSArray *subPaths = [fileManager subpathsOfDirectoryAtPath:containerDirectoryPath error:&error];
         if (error) {
-            [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" 
-                                                    message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", containerDirectoryPath] 
-                                                      error:error];
-            [activityFacility startAndEndActivityWithType:PithosActivityOther message:@"Sync: Failed to read contents of sync directory"];
+            [self fileActionFailedAlertWithTitle:@"Directory Contents Error" 
+                                         message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", containerDirectoryPath] 
+                                           error:error];
+            [self startAndEndActivityWithType:PithosActivityOther message:@"Sync: Failed to read contents of sync directory"];
             @synchronized(self) {
-                // Since the local listing failed, the operation finished and the sync cycle is completeted unsuccesfully
+                // Since the local listing failed, the operation finished and the sync cycle is completeted unsuccessfully
                 syncOperationCount = 0;
                 if (newSyncRequested)
                     [self sync];
     } else {
         NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
         if (retries > 0) {
-            ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:containerRequest];
+            ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[[PithosUtilities copyRequest:containerRequest] autorelease];
             [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-            [queue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
+            [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
         } else {
-            [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
-                              withMessage:[containerRequest.userInfo objectForKey:@"failedActivityMessage"]];
+            [self endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
+                  withMessage:[containerRequest.userInfo objectForKey:@"failedActivityMessage"]];
             @synchronized(self) {
                 // Since the server listing failed in all retries, the operation finished and the sync cycle is completeted unsuccesfully
                 syncOperationCount = 0;
             }
         }
     }
+    [pool drain];
 }
 
 - (void)listRequestFailed:(ASIPithosContainerRequest *)containerRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     if ([containerRequest isCancelled]) {
-        [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[containerRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+        [self endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
+              withMessage:[containerRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
         [objects release];
         objects = nil;
         @synchronized(self) {
     // If the server listing fails, the sync should start over, so just retrying is enough
     NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
     if (retries > 0) {
-        ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:containerRequest];
+        ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[[PithosUtilities copyRequest:containerRequest] autorelease];
         [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-        [queue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
+        [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
     } else {
-        [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[containerRequest.userInfo objectForKey:@"failedActivityMessage"]];
+        [self endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
+              withMessage:[containerRequest.userInfo objectForKey:@"failedActivityMessage"]];
         [objects release];
         objects = nil;
         @synchronized(self) {
                 [self sync];
         }
     }
+    [pool drain];
 }
 
 - (void)downloadObjectBlockFinished:(ASIPithosObjectRequest *)objectRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLog(@"Sync::download object block finished: %@", objectRequest.url);
     if (objectRequest.responseStatusCode == 206) {
         ASIPithosObject *object = [objectRequest.userInfo objectForKey:@"pithosObject"];
         
         NSString *downloadsDirPath = self.tempDownloadsDirPath;
         if (!downloadsDirPath) {
-            [activityFacility endActivity:activity 
-                              withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
+            [self endActivity:activity 
+                  withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
             @synchronized(self) {
                 syncIncomplete = YES;
                 [self decreaseSyncOperationCount];
             NSString *tempFilePath = [fileManager stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)];
             free(tempFileNameCString);
             if (fileDescriptor == -1) {
-                [PithosUtilities fileActionFailedAlertWithTitle:@"Create Temporary File Error" 
-                                                        message:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", storedState.filePath] 
-                                                          error:nil];
-                [activityFacility endActivity:activity 
-                                  withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
+                [self fileActionFailedAlertWithTitle:@"Create Temporary File Error" 
+                                             message:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", storedState.filePath] 
+                                               error:nil];
+                [self endActivity:activity 
+                      withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
                 @synchronized(self) {
                     syncIncomplete = YES;
                     [self decreaseSyncOperationCount];
             NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"];
             NSString *dirPath = [filePath stringByDeletingLastPathComponent];
             if ([fileManager fileExistsAtPath:filePath] && ![self moveToTempTrashFile:filePath]) {
-                [activityFacility endActivity:activity 
-                                  withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
+                [self endActivity:activity 
+                      withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
                 @synchronized(self) {
                     syncIncomplete = YES;
                     [self decreaseSyncOperationCount];
                 error = nil;
                 [fileManager createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:nil];
                 if (error != nil) {
-                    [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" 
-                                                            message:[NSString stringWithFormat:@"Cannot create directory at '%@'", dirPath] 
-                                                              error:error];
-                    [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                                      withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
+                    [self fileActionFailedAlertWithTitle:@"Create Directory Error" 
+                                                 message:[NSString stringWithFormat:@"Cannot create directory at '%@'", dirPath] 
+                                                   error:error];
+                    [self endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
+                          withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
                     @synchronized(self) {
                         syncIncomplete = YES;
                         [self decreaseSyncOperationCount];
             error = nil;
             [fileManager moveItemAtPath:storedState.filePath toPath:filePath error:&error];
             if (error != nil) {
-                [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" 
-                                                        message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", storedState.filePath, filePath] 
-                                                          error:error];
-                [activityFacility endActivity:activity 
-                                  withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
+                [self fileActionFailedAlertWithTitle:@"Move File Error" 
+                                             message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", storedState.filePath, filePath] 
+                                               error:error];
+                [self endActivity:activity 
+                      withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
                 @synchronized(self) {
                     syncIncomplete = YES;
                     [self decreaseSyncOperationCount];
                 }
                 return;
             }
-            [activityFacility endActivity:activity 
-                              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] 
-                               totalBytes:activity.totalBytes 
-                             currentBytes:activity.totalBytes];
+            [self endActivity:activity 
+                  withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] 
+                   totalBytes:activity.totalBytes 
+                 currentBytes:activity.totalBytes];
 
             storedState.hash = object.hash;
             storedState.filePath = nil;
             return;
         } else {
             if (newSyncRequested) {
-                [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                                  withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+                [self endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
+                      withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
                 @synchronized(self) {
                     syncIncomplete = YES;
                     [self decreaseSyncOperationCount];
                                                                                                                  blockIndex:missingBlockIndex 
                                                                                                                   blockSize:blockSize];
                 newObjectRequest.delegate = self;
-                newObjectRequest.didFinishSelector = @selector(downloadObjectBlockFinished:);
-                newObjectRequest.didFailSelector = @selector(downloadObjectBlockOrHashMapFailed:);
+                newObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+                newObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
                 newObjectRequest.userInfo = objectRequest.userInfo;
                 [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"];
                 [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
                                           totalBytes:activity.totalBytes 
                                         currentBytes:(activity.currentBytes + size)];
                 }];
-                [queue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]];
+                [networkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]];
             }
         }
     } else if (objectRequest.responseStatusCode == 412) {
         // The object has changed on the server
-        [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+        [self endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
+              withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
         @synchronized(self) {
             syncIncomplete = YES;
             [self decreaseSyncOperationCount];
     } else {
         [self requestFailed:objectRequest];
     }
+    [pool drain];
 }
 
 - (void)downloadObjectHashMapFinished:(ASIPithosObjectRequest *)objectRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLog(@"Sync::download object hashmap finished: %@", objectRequest.url);
     if (objectRequest.responseStatusCode == 200) {
         if (newSyncRequested) {
-            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                              withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+            [self endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
+                  withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
             @synchronized(self) {
                 syncIncomplete = YES;
                 [self decreaseSyncOperationCount];
                                                                     blockHash:blockHash 
                                                                    withHashes:[objectRequest hashes]];
             NSUInteger missingBlockIndex = [missingBlocks firstIndex];
-            [activityFacility endActivity:activity 
-                              withMessage:[NSString stringWithFormat:@"Sync: Downloading '%@' (%.0f%%)", 
-                                           object.name, 
-                                           (100*(activity.totalBytes - [missingBlocks count]*blockSize + 0.0)/(activity.totalBytes + 0.0))] 
-                               totalBytes:activity.totalBytes 
-                             currentBytes:(activity.totalBytes - [missingBlocks count]*blockSize)];
+            [self endActivity:activity 
+                  withMessage:[NSString stringWithFormat:@"Sync: Downloading '%@' (%.0f%%)", 
+                               object.name, 
+                               (100*(activity.totalBytes - [missingBlocks count]*blockSize + 0.0)/(activity.totalBytes + 0.0))] 
+                   totalBytes:activity.totalBytes 
+                 currentBytes:(activity.totalBytes - [missingBlocks count]*blockSize)];
 
             __block ASIPithosObjectRequest *newObjectRequest = [PithosUtilities objectBlockDataRequestWithContainerName:containerName 
                                                                                                                  object:object 
                                                                                                              blockIndex:missingBlockIndex 
                                                                                                               blockSize:blockSize];
             newObjectRequest.delegate = self;
-            newObjectRequest.didFinishSelector = @selector(downloadObjectBlockFinished:);
-            newObjectRequest.didFailSelector = @selector(downloadObjectBlockOrHashMapFailed:);
+            newObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
+            newObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
             newObjectRequest.userInfo = objectRequest.userInfo;
             [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:missingBlocks forKey:@"missingBlocks"];
             [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:missingBlockIndex] forKey:@"missingBlockIndex"];
             [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
+            [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:NSStringFromSelector(@selector(downloadObjectBlockFinished:)) forKey:@"didFinishSelector"];
             [newObjectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
                 [activityFacility updateActivity:activity 
                                      withMessage:[NSString stringWithFormat:@"Sync: Downloading '%@' (%.0f%%)", 
                                       totalBytes:activity.totalBytes 
                                     currentBytes:(activity.currentBytes + size)];
             }];
-            [queue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]];
+            [networkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]];
         }
     } else {
         [self requestFailed:objectRequest];
     }
+    [pool drain];
 }
 
 - (void)uploadDirectoryObjectFinished:(ASIPithosObjectRequest *)objectRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLog(@"Sync::upload directory object finished: %@", objectRequest.url);
     if (objectRequest.responseStatusCode == 201) {
         PithosLocalObjectState *storedState = [storedLocalObjectStates objectForKey:[[objectRequest.userInfo objectForKey:@"pithosObject"] name]];
         storedState.isDirectory = YES;
         [self saveLocalState];
-        [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+        [self endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
+              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
         @synchronized(self) {
             [self decreaseSyncOperationCount];
             if (newSyncRequested && !syncOperationCount)
     } else {
         [self requestFailed:objectRequest];
     }
+    [pool drain];
 }
 
 - (void)moveObjectToTrashFinished:(ASIPithosObjectRequest *)objectRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLog(@"Sync::move object to trash finished: %@", objectRequest.url);
     if (objectRequest.responseStatusCode == 201) {
         [storedLocalObjectStates removeObjectForKey:[[objectRequest.userInfo objectForKey:@"pithosObject"] name]];
         [self saveLocalState];
-        [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+        [self endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
+              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
         @synchronized(self) {
             [self decreaseSyncOperationCount];
             if (newSyncRequested && !syncOperationCount)
     } else {
         [self requestFailed:objectRequest];
     }
+    [pool drain];
 }
 
 - (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLog(@"Sync::upload using hashmap finished: %@", objectRequest.url);
     ASIPithosObject *object = [objectRequest.userInfo objectForKey:@"pithosObject"];
     PithosLocalObjectState *storedState = [storedLocalObjectStates objectForKey:object.name];
         NSLog(@"Sync::object created: %@", objectRequest.url);
         storedState.hash = [objectRequest eTag];
         [self saveLocalState];
-        [activityFacility endActivity:activity 
-                          withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] 
-                           totalBytes:totalBytes 
-                         currentBytes:totalBytes];
+        [self endActivity:activity 
+              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] 
+               totalBytes:totalBytes 
+             currentBytes:totalBytes];
         @synchronized(self) {
             [self decreaseSyncOperationCount];
             if (newSyncRequested && !syncOperationCount)
         }
     } else if (objectRequest.responseStatusCode == 409) {
         if (newSyncRequested) {
-            [activityFacility endActivity:activity 
-                              withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+            [self endActivity:activity 
+                  withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
             @synchronized(self) {
                 syncIncomplete = YES;
                 [self decreaseSyncOperationCount];
             NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue];
             if (iteration == 0) {
                 NSLog(@"Sync::upload iteration limit reached: %@", objectRequest.url);
-                [activityFacility endActivity:activity 
-                                  withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+                [self endActivity:activity 
+                      withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
                 syncIncomplete = YES;
                 [self decreaseSyncOperationCount];
                 return;
                                                       withMissingHashesResponse:[objectRequest responseString]];
             if (totalBytes >= [missingBlocks count]*blockSize)
                 currentBytes = totalBytes - [missingBlocks count]*blockSize;
-            [activityFacility updateActivity:activity 
-                                 withMessage:[NSString stringWithFormat:@"Sync: Uploading '%@' (%.0f%%)", object.name, (100*(currentBytes + 0.0)/(totalBytes + 0.0))] 
-                                  totalBytes:totalBytes 
-                                currentBytes:currentBytes];
+            [self updateActivity:activity 
+                     withMessage:[NSString stringWithFormat:@"Sync: Uploading '%@' (%.0f%%)", object.name, (100*(currentBytes + 0.0)/(totalBytes + 0.0))] 
+                      totalBytes:totalBytes 
+                    currentBytes:currentBytes];
             NSUInteger missingBlockIndex = [missingBlocks firstIndex];
             __block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithContainerName:containerName 
                                                                                                                         blockSize:blockSize 
                                                                                                                 missingBlockIndex:missingBlockIndex 
                                                                                                                    sharingAccount:nil];
             newContainerRequest.delegate = self;
-            newContainerRequest.didFinishSelector = @selector(uploadMissingBlockFinished:);
-            newContainerRequest.didFailSelector = @selector(requestFailed:);
+            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:@"Sync: Uploading '%@' (%.0f%%)", object.name, (100*(activity.currentBytes + size + 0.0)/(activity.totalBytes + 0.0))] 
                                       totalBytes:activity.totalBytes 
                                     currentBytes:(activity.currentBytes + size)];
             }];
-            [queue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
+            [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
         }
     } else {
         [self requestFailed:objectRequest];
     }
+    [pool drain];
 }
 
 - (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLog(@"Sync::upload of missing block finished: %@", containerRequest.url);
     if (containerRequest.responseStatusCode == 202) {
         ASIPithosObject *object = [containerRequest.userInfo objectForKey:@"pithosObject"];
                                                                                                          hashes:&hashes 
                                                                                                  sharingAccount:nil];
             newObjectRequest.delegate = self;
-            newObjectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
-            newObjectRequest.didFailSelector = @selector(requestFailed:);
+            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"];
-            [queue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]];
+            [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)) forKey:@"didFinishSelector"];
+            [networkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]];
         } else {
             if (newSyncRequested) {
-                [activityFacility endActivity:activity 
-                                  withMessage:[containerRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+                [self endActivity:activity 
+                      withMessage:[containerRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
                 @synchronized(self) {
                     syncIncomplete = YES;
                     [self decreaseSyncOperationCount];
                                                                                                                     missingBlockIndex:missingBlockIndex 
                                                                                                                        sharingAccount:nil];
                 newContainerRequest.delegate = self;
-                newContainerRequest.didFinishSelector = @selector(uploadMissingBlockFinished:);
-                newContainerRequest.didFailSelector = @selector(requestFailed:);
+                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"];
                                           totalBytes:activity.totalBytes 
                                         currentBytes:(activity.currentBytes + size)];
                 }];
-                [queue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
+                [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
             }
         }
     } else {
         [self requestFailed:containerRequest];
     }
+    [pool drain];
 }
 
 - (void)requestFailed:(ASIPithosRequest *)request {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     if ([request isCancelled]) {
-        [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
-                          withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+        [self endActivity:[request.userInfo objectForKey:@"activity"] 
+              withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
         syncIncomplete = YES;
         [self decreaseSyncOperationCount];
         return;
     }
     if (newSyncRequested) {
-        [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
-                          withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+        [self endActivity:[request.userInfo objectForKey:@"activity"] 
+              withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
         @synchronized(self) {
             syncIncomplete = YES;
             [self decreaseSyncOperationCount];
     }
     NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
     if (retries > 0) {
-        ASIPithosRequest *newRequest = (ASIPithosRequest *)[PithosUtilities copyRequest:request];
+        ASIPithosRequest *newRequest = (ASIPithosRequest *)[[PithosUtilities copyRequest:request] autorelease];
         [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-        [queue addOperation:[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]]];
+        [networkQueue addOperation:[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]]];
     } else {
-        [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
-                          withMessage:[request.userInfo objectForKey:@"failedActivityMessage"]];
+        [self endActivity:[request.userInfo objectForKey:@"activity"] 
+              withMessage:[request.userInfo objectForKey:@"failedActivityMessage"]];
         syncIncomplete = YES;
         [self decreaseSyncOperationCount];
     }
+    [pool drain];
 }