Modify sync to use a serial dispatch queue instead of threads for ASIPithosRequest...
authorMiltiadis Vasilakis <mvasilak@gmail.com>
Tue, 3 Jan 2012 13:31:46 +0000 (15:31 +0200)
committerMiltiadis Vasilakis <mvasilak@gmail.com>
Tue, 3 Jan 2012 13:31:46 +0000 (15:31 +0200)
pithos-macos/PithosSyncDaemon.h
pithos-macos/PithosSyncDaemon.m

index 1fc3843..906756e 100644 (file)
@@ -63,6 +63,7 @@
     BOOL syncIncomplete;
     
     ASINetworkQueue *networkQueue;
+    dispatch_queue_t queue;
     NSTimer *timer;
     
     PithosActivityFacility *activityFacility;
index 9d355ac..fe5f9ad 100644 (file)
         networkQueue.shouldCancelAllRequestsOnFailure = NO;
         [networkQueue go];
         
+        queue = dispatch_queue_create("gr.grnet.pithos.SyncQueue", NULL);
+        
         [[NSNotificationCenter defaultCenter] addObserver:self
                                                  selector:@selector(applicationWillTerminate:)
                                                      name:NSApplicationWillTerminateNotification
 
 - (void)dealloc {
     [[NSNotificationCenter defaultCenter] removeObserver:self];
+    dispatch_release(queue);
     [networkQueue cancelAllOperations];
     [networkQueue release];
     [timer invalidate];
 }
 
 #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 {
         if (!syncOperationCount) {
             if (!syncIncomplete) {
                 self.lastCompletedSync = [NSDate date];
-                [self startAndEndActivityWithType:PithosActivityOther 
-                                          message:[NSString stringWithFormat:@"Sync: Completed %@", lastCompletedSync]];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [activityFacility startAndEndActivityWithType:PithosActivityOther 
+                                                          message:[NSString stringWithFormat:@"Sync: Completed %@", lastCompletedSync]];
+
+                });
             }
             [self emptyTempTrash];
         }
 //        NSArray *subPaths = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:trashDirPath error:&error];
         NSArray *subPaths = [fileManager contentsOfDirectoryAtPath:trashDirPath error:&error];
         if (error) {
-            [self fileActionFailedAlertWithTitle:@"Directory Contents Error" 
-                                         message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", containerDirectoryPath] 
-                                           error:error];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" 
+                                                        message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", containerDirectoryPath] 
+                                                          error:error];
+            });
             [pool drain];
             return;
         }
 //            syncOperationCount = 1;
 //            [[NSWorkspace sharedWorkspace] recycleURLs:subURLs completionHandler:^(NSDictionary *newURLs, NSError *error) {
 //                if (error) {
-//                    [self fileActionFailedAlertWithTitle:@"Move to Trash Error" 
-//                                                 message:@"Cannot move files to Trash" 
-//                                                   error:error];
+//                    dispatch_async(dispatch_get_main_queue(), ^{
+//                        [PithosUtilities 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)
-                    [self fileActionFailedAlertWithTitle:@"Remove File Error" 
-                                                 message:[NSString stringWithFormat:@"Cannot remove file at '%@'", subFilePath] 
-                                                   error:error];
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        [PithosUtilities 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) {
-            [self fileActionFailedAlertWithTitle:@"Directory Contents Error" 
-                                         message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", containerDirectoryPath] 
-                                           error:error];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error" 
+                                                        message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", containerDirectoryPath] 
+                                                          error:error];
+            });
             [pool drain];
             return NO;
         }
         if (![fileManager createDirectoryAtPath:newDirPath withIntermediateDirectories:YES attributes:nil error:&error] || error) {
-            [self fileActionFailedAlertWithTitle:@"Create Directory Error" 
-                                         message:[NSString stringWithFormat:@"Cannot create directory at '%@'", newDirPath] 
-                                           error:error];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" 
+                                                        message:[NSString stringWithFormat:@"Cannot create directory at '%@'", newDirPath] 
+                                                          error:error];
+            });
             [pool drain];
             return NO;
         }
         if (![fileManager moveItemAtPath:filePath toPath:newFilePath error:&error] || error) {
-            [self fileActionFailedAlertWithTitle:@"Move File Error" 
-                                         message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", 
-                                                  filePath, newFilePath] 
-                                           error:error];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" 
+                                                        message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", 
+                                                                 filePath, newFilePath] 
+                                                          error:error];
+            });
             [pool drain];
             return NO;
         }
         }
     } else if (fileExists && !isDirectory) {
         if (![fileManager createDirectoryAtPath:newDirPath withIntermediateDirectories:YES attributes:nil error:&error] || error) {
-            [self fileActionFailedAlertWithTitle:@"Create Directory Error" 
-                                         message:[NSString stringWithFormat:@"Cannot create directory at '%@'", newDirPath] 
-                                           error:error];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" 
+                                                        message:[NSString stringWithFormat:@"Cannot create directory at '%@'", newDirPath] 
+                                                          error:error];
+            });
             [pool drain];
             return NO;
         }
         if (![fileManager moveItemAtPath:filePath toPath:newFilePath error:&error] || error) {
-            [self fileActionFailedAlertWithTitle:@"Move File Error" 
-                                         message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", 
-                                                  filePath, newFilePath] 
-                                           error:error];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" 
+                                                        message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", 
+                                                                 filePath, newFilePath] 
+                                                          error:error];
+            });
             [pool drain];
             return NO;
         }
             [fileManager fileExistsAtPath:localFilePath isDirectory:&isDirectory] && !isDirectory) {
             if ([localFilePath hasPrefix:containerDirectoryPath]) {
                 if (![fileManager copyItemAtPath:localFilePath toPath:filePath error:&error] || error) {
-                    [self fileActionFailedAlertWithTitle:@"Copy File Error" 
-                                                 message:[NSString stringWithFormat:@"Cannot copy file at '%@' to '%@'", 
-                                                          localFilePath, filePath] 
-                                                   error:error];
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        [PithosUtilities fileActionFailedAlertWithTitle:@"Copy File Error" 
+                                                                message:[NSString stringWithFormat:@"Cannot copy file at '%@' to '%@'", 
+                                                                         localFilePath, filePath] 
+                                                                  error:error];
+                    });
                 } else {
                     [pool drain];
                     return YES;
                 }            
             } else if (self.tempTrashDirPath && [localFilePath hasPrefix:self.tempTrashDirPath]) {
                 if (![fileManager moveItemAtPath:localFilePath toPath:filePath error:&error] || error) {
-                    [self fileActionFailedAlertWithTitle:@"Move File Error" 
-                                                 message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", 
-                                                          localFilePath, filePath] 
-                                                   error:error];
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        [PithosUtilities 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];
         }
         NSLog(@"Sync::delete local object: %@", filePath);
         if (!fileExists || [self moveToTempTrashFile:filePath]) {
-            [self startAndEndActivityWithType:PithosActivityOther 
-                                      message:[NSString stringWithFormat:@"Sync: Deleting '%@' locally (finished)", object.name]];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility 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) {
-                [self fileActionFailedAlertWithTitle:@"Create Directory Error" 
-                                             message:[NSString stringWithFormat:@"Cannot create directory at '%@'", filePath] 
-                                               error:error];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" 
+                                                            message:[NSString stringWithFormat:@"Cannot create directory at '%@'", filePath] 
+                                                              error:error];
+                });
             } else {
                 directoryCreated = YES;
                 storedState.isDirectory = YES;
             directoryCreated = YES;
         }
         if (directoryCreated)
-            [self startAndEndActivityWithType:PithosActivityOther 
-                                      message:[NSString stringWithFormat:@"Sync: Creating directory '%@' locally (finished)", object.name]];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility startAndEndActivityWithType:PithosActivityOther 
+                                                      message:[NSString stringWithFormat:@"Sync: Creating directory '%@' locally (finished)", object.name]];
+            });
     } else if (object.bytes == 0) {
         // Create local object with zero length
         if (!remoteObjectExists || remoteIsDirectory) {
             // Create directory of the file, if it doesn't exist
             error = nil;
             if (![PithosUtilities safeCreateDirectory:fileDirectoryPath error:&error]) {
-                [self fileActionFailedAlertWithTitle:@"Create Directory Error" 
-                                             message:[NSString stringWithFormat:@"Cannot create directory at '%@'", fileDirectoryPath] 
-                                               error:error];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" 
+                                                            message:[NSString stringWithFormat:@"Cannot create directory at '%@'", fileDirectoryPath] 
+                                                              error:error];
+                });
             }
             error = nil;
             if (![fileManager createFileAtPath:filePath contents:nil attributes:nil]) {
-                [self fileActionFailedAlertWithTitle:@"Create File Error" 
-                                             message:[NSString stringWithFormat:@"Cannot create file at '%@'", filePath] 
-                                               error:error];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [PithosUtilities fileActionFailedAlertWithTitle:@"Create File Error" 
+                                                            message:[NSString stringWithFormat:@"Cannot create file at '%@'", filePath] 
+                                                              error:error];
+                });
             } else {
                 fileCreated = YES;
                 storedState.hash = object.objectHash;
             fileCreated = YES;
         }
         if (fileCreated)
-            [self startAndEndActivityWithType:PithosActivityOther 
-                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility startAndEndActivityWithType:PithosActivityOther 
+                                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
+            });
     } else if (storedState.tmpFilePath == nil) {
         // Create new local object
         if (!remoteObjectExists || remoteIsDirectory) {
         // Create directory of the file, if it doesn't exist
         error = nil;
         if (![PithosUtilities safeCreateDirectory:fileDirectoryPath error:&error]) {
-            [self fileActionFailedAlertWithTitle:@"Create Directory Error" 
-                                         message:[NSString stringWithFormat:@"Cannot create directory at '%@'", fileDirectoryPath] 
-                                           error:error];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" 
+                                                        message:[NSString stringWithFormat:@"Cannot create directory at '%@'", fileDirectoryPath] 
+                                                          error:error];
+            });
         }
         // Check first if a local copy exists
         if ([self findLocalCopyForObjectWithHash:object.objectHash forFile:filePath]) {
-            [self startAndEndActivityWithType:PithosActivityOther 
-                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility startAndEndActivityWithType:PithosActivityOther 
+                                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
+            });
         } else {
             [self increaseSyncOperationCount];
             __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectBlockDataRequestWithContainerName:containerName 
                                                                        message:[NSString stringWithFormat:@"Sync: Downloading '%@' (0%%)", object.name] 
                                                                     totalBytes:object.bytes 
                                                                   currentBytes:0];
-            [self updateActivity:activity withMessage:activity.message];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility 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", 
         // Create directory of the file, if it doesn't exist
         error = nil;
         if (![PithosUtilities safeCreateDirectory:fileDirectoryPath error:&error]) {
-            [self fileActionFailedAlertWithTitle:@"Create Directory Error" 
-                                         message:[NSString stringWithFormat:@"Cannot create directory at '%@'", fileDirectoryPath] 
-                                           error:error];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [PithosUtilities fileActionFailedAlertWithTitle:@"Create Directory Error" 
+                                                        message:[NSString stringWithFormat:@"Cannot create directory at '%@'", fileDirectoryPath] 
+                                                          error:error];
+            });
         }
         // Check first if a local copy exists
         if ([self findLocalCopyForObjectWithHash:object.objectHash forFile:filePath]) {
-            [self startAndEndActivityWithType:PithosActivityOther 
-                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility startAndEndActivityWithType:PithosActivityOther 
+                                                      message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
+            });
             // Delete incomplete temp download
             error = nil;
             storedState.tmpFilePath = nil;
                                                                        message:[NSString stringWithFormat:@"Sync: Downloading '%@' (0%%)", object.name] 
                                                                     totalBytes:object.bytes 
                                                                   currentBytes:0];
-            [self updateActivity:activity withMessage:activity.message];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility updateActivity:activity withMessage:activity.message];
+            });
             objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                       object, @"pithosObject", 
                                       filePath, @"filePath", 
         objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
         PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory 
                                                                    message:[NSString stringWithFormat:@"Sync: Creating directory '%@'", object.name]];
-        [self updateActivity:activity withMessage:activity.message];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility updateActivity:activity withMessage:activity.message];
+        });
         objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                   object, @"pithosObject", 
                                   activity, @"activity", 
                 objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
                 PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete 
                                                                            message:[NSString stringWithFormat:@"Sync: Moving to trash '%@'", object.name]];
-                [self updateActivity:activity withMessage:activity.message];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [activityFacility updateActivity:activity withMessage:activity.message];
+                });
                 objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                           object, @"pithosObject", 
                                           activity, @"activity", 
                                                                        message:[NSString stringWithFormat:@"Sync: Uploading '%@' (0%%)", object.name]
                                                                     totalBytes:[[objectRequest.userInfo valueForKey:@"bytes"] unsignedIntegerValue]
                                                                   currentBytes:0];
-            [self updateActivity:activity withMessage:activity.message];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility updateActivity:activity withMessage:activity.message];
+            });
             [(NSMutableDictionary *)objectRequest.userInfo addEntriesFromDictionary:
              [NSDictionary dictionaryWithObjectsAndKeys:
               object, @"pithosObject", 
 #pragma mark ASIHTTPRequestDelegate
 
 - (void)performRequestFinishedDelegateInBackground:(ASIPithosRequest *)request {
-    [self performSelectorInBackground:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) withObject:request];
+    dispatch_async(queue, ^{
+        [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) withObject:request];
+    });
 }
 
 - (void)performRequestFailedDelegateInBackground:(ASIPithosRequest *)request {
-    [self performSelectorInBackground:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) withObject:request];
+    dispatch_async(queue, ^{
+        [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) withObject:request];
+    });
 }
 
 - (void)listRequestFinished:(ASIPithosContainerRequest *)containerRequest {
                 return;
             }
         }
-        [self endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
-              withMessage:[containerRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility 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) {
-            [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"];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [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"];
+            });
             @synchronized(self) {
                 // Since the local listing failed, the operation finished and the sync cycle is completeted unsuccessfully
                 syncOperationCount = 0;
             [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
             [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
         } else {
-            [self endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
-                  withMessage:[containerRequest.userInfo objectForKey:@"failedActivityMessage"]];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility 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;
 - (void)listRequestFailed:(ASIPithosContainerRequest *)containerRequest {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     if ([containerRequest isCancelled]) {
-        [self endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
-              withMessage:[containerRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
+                              withMessage:[containerRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+        });
         [objects release];
         objects = nil;
         @synchronized(self) {
         [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
         [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
     } else {
-        [self endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
-              withMessage:[containerRequest.userInfo objectForKey:@"failedActivityMessage"]];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
+                              withMessage:[containerRequest.userInfo objectForKey:@"failedActivityMessage"]];
+        });
         [objects release];
         objects = nil;
         @synchronized(self) {
         
         NSString *downloadsDirPath = self.tempDownloadsDirPath;
         if (!downloadsDirPath) {
-            [self endActivity:activity 
-                  withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility 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) {
-                [self fileActionFailedAlertWithTitle:@"Create Temporary File Error" 
-                                             message:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", tempFilePath] 
-                                               error:nil];
-                [self endActivity:activity 
-                      withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [PithosUtilities fileActionFailedAlertWithTitle:@"Create Temporary File Error" 
+                                                            message:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", tempFilePath] 
+                                                              error:nil];
+                    [activityFacility 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]) {
-                [self endActivity:activity 
-                      withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [activityFacility 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) {
-                    [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"]];
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        [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"]];
+                    });
                     @synchronized(self) {
                         syncIncomplete = YES;
                         [self decreaseSyncOperationCount];
             error = nil;
             [fileManager moveItemAtPath:storedState.tmpFilePath toPath:filePath error:&error];
             if (error != nil) {
-                [self fileActionFailedAlertWithTitle:@"Move File Error" 
-                                             message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", storedState.tmpFilePath, filePath] 
-                                               error:error];
-                [self endActivity:activity 
-                      withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [PithosUtilities fileActionFailedAlertWithTitle:@"Move File Error" 
+                                                            message:[NSString stringWithFormat:@"Cannot move file at '%@' to '%@'", storedState.tmpFilePath, filePath] 
+                                                              error:error];
+                    [activityFacility endActivity:activity 
+                                      withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
+                });
                 @synchronized(self) {
                     syncIncomplete = YES;
                     [self decreaseSyncOperationCount];
                 [pool drain];
                 return;
             }
-            [self endActivity:activity 
-                  withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] 
-                   totalBytes:activity.totalBytes 
-                 currentBytes:activity.totalBytes];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility endActivity:activity 
+                                  withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] 
+                                   totalBytes:activity.totalBytes 
+                                 currentBytes:activity.totalBytes];
+            });
 
             storedState.hash = object.objectHash;
             storedState.tmpFilePath = nil;
             return;
         } else {
             if (newSyncRequested) {
-                [self endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                      withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
+                                      withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+                });
                 @synchronized(self) {
                     syncIncomplete = YES;
                     [self decreaseSyncOperationCount];
         }
     } else if (objectRequest.responseStatusCode == 412) {
         // The object has changed on the server
-        [self endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-              withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
+                              withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+        });
         @synchronized(self) {
             syncIncomplete = YES;
             [self decreaseSyncOperationCount];
     NSLog(@"Sync::download object hashmap finished: %@", objectRequest.url);
     if (objectRequest.responseStatusCode == 200) {
         if (newSyncRequested) {
-            [self endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-                  withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility 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];
-            [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)];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [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)];
+            });
 
             __block ASIPithosObjectRequest *newObjectRequest = [PithosUtilities objectBlockDataRequestWithContainerName:containerName 
                                                                                                                  object:object 
         PithosLocalObjectState *storedState = [storedLocalObjectStates objectForKey:[[objectRequest.userInfo objectForKey:@"pithosObject"] name]];
         storedState.isDirectory = YES;
         [self saveLocalState];
-        [self endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
+                              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+        });
         @synchronized(self) {
             [self decreaseSyncOperationCount];
             if (newSyncRequested && !syncOperationCount)
     if (objectRequest.responseStatusCode == 201) {
         [storedLocalObjectStates removeObjectForKey:[[objectRequest.userInfo objectForKey:@"pithosObject"] name]];
         [self saveLocalState];
-        [self endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
-              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
+                              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
+        });
         @synchronized(self) {
             [self decreaseSyncOperationCount];
             if (newSyncRequested && !syncOperationCount)
         NSLog(@"Sync::object created: %@", objectRequest.url);
         storedState.hash = [objectRequest objectHash];
         [self saveLocalState];
-        [self endActivity:activity 
-              withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"] 
-               totalBytes:totalBytes 
-             currentBytes:totalBytes];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility 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) {
-            [self endActivity:activity 
-                  withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility 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);
-                [self endActivity:activity 
-                      withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [activityFacility endActivity:activity 
+                                      withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+                });
                 syncIncomplete = YES;
                 [self decreaseSyncOperationCount];
                 [pool drain];
                                                       withMissingHashesResponse:[objectRequest responseString]];
             if (totalBytes >= [missingBlocks count]*blockSize)
                 currentBytes = totalBytes - [missingBlocks count]*blockSize;
-            [self updateActivity:activity 
-                     withMessage:[NSString stringWithFormat:@"Sync: Uploading '%@' (%.0f%%)", object.name, (100*(currentBytes + 0.0)/(totalBytes + 0.0))] 
-                      totalBytes:totalBytes 
-                    currentBytes:currentBytes];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [activityFacility 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 
             [networkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]];
         } else {
             if (newSyncRequested) {
-                [self endActivity:activity 
-                      withMessage:[containerRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [activityFacility endActivity:activity 
+                                      withMessage:[containerRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+                });
                 @synchronized(self) {
                     syncIncomplete = YES;
                     [self decreaseSyncOperationCount];
 - (void)requestFailed:(ASIPithosRequest *)request {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     if ([request isCancelled]) {
-        [self endActivity:[request.userInfo objectForKey:@"activity"] 
-              withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
+                              withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+        });
         syncIncomplete = YES;
         [self decreaseSyncOperationCount];
         [pool drain];
         return;
     }
     if (newSyncRequested) {
-        [self endActivity:[request.userInfo objectForKey:@"activity"] 
-              withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
+                              withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+        });
         @synchronized(self) {
             syncIncomplete = YES;
             [self decreaseSyncOperationCount];
         [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
         [networkQueue addOperation:[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]]];
     } else {
-        [self endActivity:[request.userInfo objectForKey:@"activity"] 
-              withMessage:[request.userInfo objectForKey:@"failedActivityMessage"]];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
+                              withMessage:[request.userInfo objectForKey:@"failedActivityMessage"]];
+        });
         syncIncomplete = YES;
         [self decreaseSyncOperationCount];
     }