// or implied, of GRNET S.A.
#import "PithosSyncDaemon.h"
+#import "PithosAccount.h"
#import "PithosLocalObjectState.h"
#import "PithosActivityFacility.h"
#import "PithosUtilities.h"
#import "ASIPithosObject.h"
@interface PithosSyncDaemon (Private)
+- (void)resetLocalState;
- (NSString *)pithosStateFilePath;
- (void)saveLocalState;
- (void)updateServerStateWithCurrentState:(PithosLocalObjectState *)currentState
object:(ASIPithosObject *)object
localFilePath:(NSString *)filePath;
+- (void)listRequestFailed:(ASIPithosContainerRequest *)containerRequest;
- (void)requestFailed:(ASIPithosRequest *)request;
-- (void)increaseSyncOperationCount;
-- (void)decreaseSyncOperationCount;
+- (void)syncOperationStarted;
+- (void)syncOperationFinishedWithSuccess:(BOOL)operationSuccessfull;
@end
@implementation PithosSyncDaemon
+@synthesize directoryPath, pithosAccount, containerName, pithos;
@synthesize blockHash, blockSize, lastModified, lastCompletedSync, remoteObjects, storedLocalObjectStates, currentLocalObjectStates;
-@synthesize directoryPath, containerDirectoryPath, pithosStateFilePath, tempDownloadsDirPath, tempTrashDirPath;
+@synthesize containerDirectoryPath, pithosStateFilePath, tempDownloadsDirPath, tempTrashDirPath;
#pragma mark -
#pragma Object Lifecycle
- (id)initWithDirectoryPath:(NSString *)aDirectoryPath
- pithos:(ASIPithos *)aPithos
+ pithosAccount:(PithosAccount *)aPithosAccount
containerName:(NSString *)aContainerName
- timeInterval:(NSTimeInterval)aTimeInterval
resetLocalState:(BOOL)resetLocalState {
if ((self = [super init])) {
directoryPath = [aDirectoryPath copy];
- pithos = [aPithos retain];
+ pithosAccount = [aPithosAccount retain];
containerName = [aContainerName copy];
- timeInterval = aTimeInterval;
-
- syncOperationCount = 0;
- newSyncRequested = NO;
- containerDirectoryPath = [[directoryPath stringByAppendingPathComponent:containerName] copy];
+ self.pithos = pithosAccount.pithos;
activityFacility = [PithosActivityFacility defaultPithosActivityFacility];
- NSFileManager *fileManager = [NSFileManager defaultManager];
- if (resetLocalState) {
- NSError *error = nil;
- if ([fileManager fileExistsAtPath:self.pithosStateFilePath] &&
- (![fileManager removeItemAtPath:self.pithosStateFilePath error:&error] || error))
- [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error"
- message:[NSString stringWithFormat:@"Cannot remove file at '%@'", self.pithosStateFilePath]
- error:error];
- if (self.tempDownloadsDirPath) {
- error = nil;
- for (NSString *subPath in [fileManager contentsOfDirectoryAtPath:self.tempDownloadsDirPath error:&error]) {
- if (error) {
- [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error"
- message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", self.tempDownloadsDirPath]
- error:error];
- break;
- }
- NSString *subFilePath = [self.tempDownloadsDirPath stringByAppendingPathComponent:subPath];
- if (![fileManager removeItemAtPath:subFilePath error:&error] || error) {
- [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error"
- message:[NSString stringWithFormat:@"Cannot remove file at '%@'", subFilePath]
- error:error];
- }
- error = nil;
- }
- }
- }
- if ([[NSFileManager defaultManager] fileExistsAtPath:self.pithosStateFilePath]) {
- NSData *data = [NSData dataWithContentsOfFile:self.pithosStateFilePath];
- NSKeyedUnarchiver *unarchiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData:data] autorelease];
- self.storedLocalObjectStates = [unarchiver decodeObjectForKey:containerName];
- [unarchiver finishDecoding];
- } else {
- self.storedLocalObjectStates = [NSMutableDictionary dictionary];
- }
+ if (resetLocalState)
+ [self resetLocalState];
networkQueue = [[ASINetworkQueue alloc] init];
networkQueue.showAccurateProgress = YES;
networkQueue.shouldCancelAllRequestsOnFailure = NO;
- [networkQueue go];
+// networkQueue.maxConcurrentOperationCount = 1;
- queue = dispatch_queue_create("gr.grnet.pithos.SyncQueue", NULL);
+ callbackQueue = [[NSOperationQueue alloc] init];
+ [callbackQueue setSuspended:YES];
+ callbackQueue.name = [NSString stringWithFormat:@"gr.grnet.pithos.SyncCallbackQueue.%@", pithosAccount.uniqueName];
+// callbackQueue.maxConcurrentOperationCount = 1;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillTerminate:)
name:NSApplicationWillTerminateNotification
object:[NSApplication sharedApplication]];
+
+ [self startDaemon];
+ }
+ return self;
+}
+
+- (void)resetLocalState {
+ self.lastCompletedSync = nil;
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ NSError *error = nil;
+ if ([fileManager fileExistsAtPath:self.pithosStateFilePath] &&
+ (![fileManager removeItemAtPath:self.pithosStateFilePath error:&error] || error))
+ [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error"
+ message:[NSString stringWithFormat:@"Cannot remove file at '%@'", self.pithosStateFilePath]
+ error:error];
+ if (self.tempDownloadsDirPath) {
+ error = nil;
+ for (NSString *subPath in [fileManager contentsOfDirectoryAtPath:self.tempDownloadsDirPath error:&error]) {
+ if (error) {
+ [PithosUtilities fileActionFailedAlertWithTitle:@"Directory Contents Error"
+ message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", self.tempDownloadsDirPath]
+ error:error];
+ break;
+ }
+ NSString *subFilePath = [self.tempDownloadsDirPath stringByAppendingPathComponent:subPath];
+ if (![fileManager removeItemAtPath:subFilePath error:&error] || error) {
+ [PithosUtilities fileActionFailedAlertWithTitle:@"Remove File Error"
+ message:[NSString stringWithFormat:@"Cannot remove file at '%@'", subFilePath]
+ error:error];
+ }
+ error = nil;
+ }
+ }
+}
+
+- (void)resetDaemon {
+ @synchronized(self) {
+ if (!daemonActive)
+ return;
+ }
- timer = [[NSTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(sync) userInfo:nil repeats:YES] retain];
- [timer fire];
+ [networkQueue reset];
+ [callbackQueue cancelAllOperations];
+ [callbackQueue setSuspended:YES];
+ [self emptyTempTrash];
+
+ @synchronized(self) {
+ daemonActive = NO;
+ }
+}
+
+- (void)startDaemon {
+ @synchronized(self) {
+ if (daemonActive)
+ return;
}
- return self;
+ syncOperationCount = 0;
+ newSyncRequested = NO;
+ syncIncomplete = NO;
+ syncLate = NO;
+
+ self.containerDirectoryPath = [directoryPath stringByAppendingPathComponent:containerName];
+
+ if ([[NSFileManager defaultManager] fileExistsAtPath:self.pithosStateFilePath]) {
+ NSData *data = [NSData dataWithContentsOfFile:self.pithosStateFilePath];
+ NSKeyedUnarchiver *unarchiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData:data] autorelease];
+ self.storedLocalObjectStates = [unarchiver decodeObjectForKey:containerName];
+ [unarchiver finishDecoding];
+ } else {
+ self.storedLocalObjectStates = [NSMutableDictionary dictionary];
+ }
+
+ // In the improbable case of leftover operations
+ [networkQueue reset];
+ [callbackQueue cancelAllOperations];
+
+ [networkQueue go];
+ [callbackQueue setSuspended:NO];
+
+ @synchronized(self) {
+ daemonActive = YES;
+ }
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
- dispatch_release(queue);
- [networkQueue cancelAllOperations];
+ [self resetDaemon];
+ [callbackQueue release];
[networkQueue release];
- [timer invalidate];
- [timer release];
[tempTrashDirPath release];
[tempDownloadsDirPath release];
[pithosStateFilePath release];
[lastCompletedSync release];
[lastModified release];
[blockHash release];
- [containerName release];
[pithos release];
+ [containerName release];
+ [pithosAccount release];
[directoryPath release];
[super dealloc];
}
if (!pithosStateFilePath)
pithosStateFilePath = [[[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]
stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]]
- stringByAppendingPathComponent:@"PithosLocalObjectStates.archive"] retain];
+ stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-PithosLocalObjectStates.archive",
+ pithosAccount.uniqueName]] retain];
return [[pithosStateFilePath copy] autorelease];
}
- (NSString *)tempDownloadsDirPath {
- NSFileManager *fileManager = [NSFileManager defaultManager];
- if (!tempDownloadsDirPath || ![fileManager fileExistsAtPath:tempDownloadsDirPath]) {
- // Get the path from user defaults
- NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
- tempDownloadsDirPath = [userDefaults stringForKey:@"PithosSyncTempDownloadsDirPath"];
- if (tempDownloadsDirPath) {
- // Check if the path exists
- BOOL isDirectory;
- BOOL fileExists = [fileManager fileExistsAtPath:tempDownloadsDirPath isDirectory:&isDirectory];
- NSError *error = nil;
- if (fileExists && !isDirectory)
- [fileManager removeItemAtPath:tempDownloadsDirPath error:&error];
- if (!error & !fileExists)
- [fileManager createDirectoryAtPath:tempDownloadsDirPath withIntermediateDirectories:YES attributes:nil error:&error];
- if (error)
- tempDownloadsDirPath = nil;
- }
- if (!tempDownloadsDirPath) {
- NSString *tempDirTemplate = [[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]
- stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]]
- stringByAppendingPathComponent:@"Temp Downloads XXXXXX"];
- const char *tempDirTemplateCString = [tempDirTemplate fileSystemRepresentation];
- char *tempDirNameCString = (char *)malloc(strlen(tempDirTemplateCString) + 1);
- strcpy(tempDirNameCString, tempDirTemplateCString);
- tempDirNameCString = mkdtemp(tempDirNameCString);
- if (tempDirNameCString != NULL)
- tempDownloadsDirPath = [fileManager stringWithFileSystemRepresentation:tempDirNameCString length:strlen(tempDirNameCString)];
- free(tempDirNameCString);
- }
- if (tempDownloadsDirPath)
- [userDefaults setObject:tempDownloadsDirPath forKey:@"PithosSyncTempDownloadsDirPath"];
- [tempDownloadsDirPath retain];
+ if (!tempDownloadsDirPath) {
+ tempDownloadsDirPath = [[[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]
+ stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]]
+ stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-TempDownloads",
+ pithosAccount.uniqueName]] retain];
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ BOOL isDirectory;
+ BOOL fileExists = [fileManager fileExistsAtPath:tempDownloadsDirPath isDirectory:&isDirectory];
+ NSError *error = nil;
+ if (fileExists && !isDirectory)
+ [fileManager removeItemAtPath:tempDownloadsDirPath error:&error];
+ if (!error & !fileExists)
+ [fileManager createDirectoryAtPath:tempDownloadsDirPath withIntermediateDirectories:YES attributes:nil error:&error];
+ //if (error)
+ // tempDownloadsDirPath = nil;
+ // XXX create a dir using mktmps?
}
- return [[tempDownloadsDirPath copy] autorelease];
+ return tempDownloadsDirPath;
}
- (NSString *)tempTrashDirPath {
- NSFileManager *fileManager = [NSFileManager defaultManager];
- if (!tempTrashDirPath || ![fileManager fileExistsAtPath:tempTrashDirPath]) {
- // Get the path from user defaults
- NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
- tempTrashDirPath = [userDefaults stringForKey:@"PithosSyncTempTrashDirPath"];
- if (tempTrashDirPath) {
- // Check if the path exists
- BOOL isDirectory;
- BOOL fileExists = [fileManager fileExistsAtPath:tempTrashDirPath isDirectory:&isDirectory];
- NSError *error = nil;
- if (fileExists && !isDirectory)
- [fileManager removeItemAtPath:tempTrashDirPath error:&error];
- if (!error & !fileExists)
- [fileManager createDirectoryAtPath:tempTrashDirPath withIntermediateDirectories:YES attributes:nil error:&error];
- if (error)
- tempTrashDirPath = nil;
- }
- if (!tempTrashDirPath) {
- NSString *tempDirTemplate = [[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]
- stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]]
- stringByAppendingPathComponent:@"Temp Trash XXXXXX"];
- const char *tempDirTemplateCString = [tempDirTemplate fileSystemRepresentation];
- char *tempDirNameCString = (char *)malloc(strlen(tempDirTemplateCString) + 1);
- strcpy(tempDirNameCString, tempDirTemplateCString);
- tempDirNameCString = mkdtemp(tempDirNameCString);
- if (tempDirNameCString != NULL)
- tempTrashDirPath = [fileManager stringWithFileSystemRepresentation:tempDirNameCString length:strlen(tempDirNameCString)];
- free(tempDirNameCString);
- }
- if (tempTrashDirPath)
- [userDefaults setObject:tempTrashDirPath forKey:@"PithosSyncTempTrashDirPath"];
- [tempTrashDirPath retain];
+ if (!tempTrashDirPath) {
+ tempTrashDirPath = [[[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]
+ stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]]
+ stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-TempTrash",
+ pithosAccount.uniqueName]] retain];
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ BOOL isDirectory;
+ BOOL fileExists = [fileManager fileExistsAtPath:tempTrashDirPath isDirectory:&isDirectory];
+ NSError *error = nil;
+ if (fileExists && !isDirectory)
+ [fileManager removeItemAtPath:tempTrashDirPath error:&error];
+ if (!error & !fileExists)
+ [fileManager createDirectoryAtPath:tempTrashDirPath withIntermediateDirectories:YES attributes:nil error:&error];
+ //if (error)
+ // tempTrashDirPath = nil;
+ // XXX create a dir using mktmps?
}
- return [[tempTrashDirPath copy] autorelease];
+ return tempTrashDirPath;
+}
+
+- (void)setDirectoryPath:(NSString *)aDirectoryPath {
+ if (aDirectoryPath && ![aDirectoryPath isEqualToString:directoryPath]) {
+ [self resetDaemon];
+ [self resetLocalState];
+ [directoryPath release];
+ directoryPath = [aDirectoryPath copy];
+ }
+}
+
+- (void)setContainerName:(NSString *)aContainerName {
+ if (aContainerName && ![aContainerName isEqualToString:containerName]) {
+ [self resetDaemon];
+ [self resetLocalState];
+ [containerName release];
+ containerName = [aContainerName copy];
+ }
+}
+
+- (void)setPithos:(ASIPithos *)aPithos {
+ if (!pithos) {
+ pithos = [[ASIPithos pithos] retain];
+ pithos.authUser = [[aPithos.authUser copy] autorelease];
+ pithos.authToken = [[aPithos.authToken copy] autorelease];
+ pithos.storageURLPrefix = [[aPithos.storageURLPrefix copy] autorelease];
+ pithos.authURL = [[aPithos.authURL copy] autorelease];
+ pithos.publicURLPrefix = [[aPithos.publicURLPrefix copy] autorelease];
+ }
+ if (aPithos &&
+ (![aPithos.authUser isEqualToString:pithos.authUser] ||
+ ![aPithos.authToken isEqualToString:pithos.authToken] ||
+ ![aPithos.storageURLPrefix isEqualToString:pithos.storageURLPrefix])) {
+ [self resetDaemon];
+ if (![aPithos.authUser isEqualToString:pithos.authUser] ||
+ ![aPithos.storageURLPrefix isEqualToString:pithos.storageURLPrefix])
+ [self resetLocalState];
+ pithos.authUser = [[aPithos.authUser copy] autorelease];
+ pithos.authToken = [[aPithos.authToken copy] autorelease];
+ pithos.storageURLPrefix = [[aPithos.storageURLPrefix copy] autorelease];
+ pithos.authURL = [[aPithos.authURL copy] autorelease];
+ pithos.publicURLPrefix = [[aPithos.publicURLPrefix copy] autorelease];
+ }
}
#pragma mark -
[pool drain];
}
-- (void)increaseSyncOperationCount {
+- (void)syncOperationStarted {
@synchronized(self) {
syncOperationCount++;
}
}
-- (void)decreaseSyncOperationCount {
+- (void)syncOperationFinishedWithSuccess:(BOOL)operationSuccessfull {
@synchronized(self) {
+ if (!operationSuccessfull)
+ syncIncomplete = YES;
+ if (syncOperationCount == 0)
+ // XXX This shouldn't happen, maybe change operationCount to a BOOL as operationsPending in browser
+ return;
syncOperationCount--;
- if (!syncOperationCount) {
+ if (syncOperationCount == 0) {
if (!syncIncomplete) {
self.lastCompletedSync = [NSDate date];
dispatch_async(dispatch_get_main_queue(), ^{
[activityFacility startAndEndActivityWithType:PithosActivityOther
- message:[NSString stringWithFormat:@"Sync: Completed %@", lastCompletedSync]];
-
+ message:[NSString stringWithFormat:@"Sync: Completed %@", lastCompletedSync]
+ pithosAccount:pithosAccount];
+
});
}
[self emptyTempTrash];
+ if (newSyncRequested && daemonActive)
+ [self sync];
}
}
}
+- (BOOL)isSyncing {
+ @synchronized(self) {
+ return ((syncOperationCount > 0) && daemonActive);
+ }
+}
+
+- (void)syncLate {
+ @synchronized(self) {
+ if ([self isSyncing])
+ syncLate = YES;
+ }
+}
+
- (void)sync {
@synchronized(self) {
- if (syncOperationCount) {
+ if ([self isSyncing]) {
// If at least one operation is running return
newSyncRequested = YES;
return;
- } else {
+ } else if (daemonActive) {
// The first operation is the server listing
- syncOperationCount = 1;
+ [self syncOperationStarted];
newSyncRequested = NO;
syncIncomplete = NO;
+ syncLate = NO;
+ } else {
+ return;
}
}
[PithosUtilities fileActionFailedAlertWithTitle:@"Local Sync Directory Error"
message:[NSString stringWithFormat:@"Cannot create local sync directory at '%@'", containerDirectoryPath]
error:error];
- @synchronized(self) {
- syncOperationCount = 0;
- }
+ [self syncOperationFinishedWithSuccess:NO];
return;
}
} else if (!isDirectory) {
[PithosUtilities fileActionFailedAlertWithTitle:@"Local Sync Directory Error"
message:[NSString stringWithFormat:@"File already exists at the local sync directory path at '%@'", containerDirectoryPath]
error:nil];
- @synchronized(self) {
- syncOperationCount = 0;
- }
+ [self syncOperationFinishedWithSuccess:NO];
return;
}
containerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
containerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityOther
- message:@"Sync: Getting server listing"];
+ message:@"Sync: Getting server listing"
+ pithosAccount:pithosAccount];
containerRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
activity, @"activity",
@"Sync: Getting server listing (stopped)", @"stoppedActivityMessage",
// for (NSString *subPath in subPaths) {
// [subURLs addObject:[NSURL fileURLWithPath:[trashDirPath stringByAppendingPathComponent:subPath]]];
// }
-// syncOperationCount = 1;
+// [self syncOperationStarted];
// [[NSWorkspace sharedWorkspace] recycleURLs:subURLs completionHandler:^(NSDictionary *newURLs, NSError *error) {
// if (error) {
// dispatch_async(dispatch_get_main_queue(), ^{
// error:error];
// });
// }
-// syncOperationCount = 0;
+// [self syncOperationFinishedWithSuccess:YES];
// }];
for (NSString *subPath in subPaths) {
NSString *subFilePath = [trashDirPath stringByAppendingPathComponent:subPath];
if (!fileExists || [self moveToTempTrashFile:filePath]) {
dispatch_async(dispatch_get_main_queue(), ^{
[activityFacility startAndEndActivityWithType:PithosActivityOther
- message:[NSString stringWithFormat:@"Sync: Deleting '%@' locally (finished)", object.name]];
+ message:[NSString stringWithFormat:@"Sync: Deleting '%@' locally (finished)", object.name]
+ pithosAccount:pithosAccount];
});
[storedLocalObjectStates removeObjectForKey:object.name];
[self saveLocalState];
if (directoryCreated)
dispatch_async(dispatch_get_main_queue(), ^{
[activityFacility startAndEndActivityWithType:PithosActivityOther
- message:[NSString stringWithFormat:@"Sync: Creating directory '%@' locally (finished)", object.name]];
+ message:[NSString stringWithFormat:@"Sync: Creating directory '%@' locally (finished)", object.name]
+ pithosAccount:pithosAccount];
});
} else if (object.bytes == 0) {
// Create local object with zero length
if (fileCreated)
dispatch_async(dispatch_get_main_queue(), ^{
[activityFacility startAndEndActivityWithType:PithosActivityOther
- message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
+ message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]
+ pithosAccount:pithosAccount];
});
} else if (storedState.tmpFilePath == nil) {
// Create new local object
[self saveLocalState];
dispatch_async(dispatch_get_main_queue(), ^{
[activityFacility startAndEndActivityWithType:PithosActivityOther
- message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
+ message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]
+ pithosAccount:pithosAccount];
});
} else {
- [self increaseSyncOperationCount];
+ [self syncOperationStarted];
__block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectBlockDataRequestWithPithos:pithos
containerName:containerName
object:object
PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload
message:[NSString stringWithFormat:@"Sync: Downloading '%@' (0%%)", object.name]
totalBytes:object.bytes
- currentBytes:0];
+ currentBytes:0
+ pithosAccount:pithosAccount];
dispatch_async(dispatch_get_main_queue(), ^{
- [activityFacility updateActivity:activity withMessage:activity.message];
+ [activityFacility updateActivity:activity withMessage:activity.message];
});
objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
object, @"pithosObject",
[self saveLocalState];
dispatch_async(dispatch_get_main_queue(), ^{
[activityFacility startAndEndActivityWithType:PithosActivityOther
- message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
+ message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]
+ pithosAccount:pithosAccount];
});
} else {
- [self increaseSyncOperationCount];
+ [self syncOperationStarted];
ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectHashmapRequestWithPithos:pithos
containerName:containerName
objectName:object.name];
PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDownload
message:[NSString stringWithFormat:@"Sync: Downloading '%@' (0%%)", object.name]
totalBytes:object.bytes
- currentBytes:0];
+ currentBytes:0
+ pithosAccount:pithosAccount];
dispatch_async(dispatch_get_main_queue(), ^{
[activityFacility updateActivity:activity withMessage:activity.message];
});
object:(ASIPithosObject *)object
localFilePath:(NSString *)filePath {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [self increaseSyncOperationCount];
+ [self syncOperationStarted];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDirectory;
BOOL fileExists = [fileManager fileExistsAtPath:filePath isDirectory:&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];
+ [self syncOperationFinishedWithSuccess:NO];
[pool drain];
return;
}
objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityCreateDirectory
- message:[NSString stringWithFormat:@"Sync: Creating directory '%@'", object.name]];
+ message:[NSString stringWithFormat:@"Sync: Creating directory '%@'", object.name]
+ pithosAccount:pithosAccount];
dispatch_async(dispatch_get_main_queue(), ^{
[activityFacility updateActivity:activity withMessage:activity.message];
});
objectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
objectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete
- message:[NSString stringWithFormat:@"Sync: Moving to trash '%@'", object.name]];
+ message:[NSString stringWithFormat:@"Sync: Moving to trash '%@'", object.name]
+ pithosAccount:pithosAccount];
dispatch_async(dispatch_get_main_queue(), ^{
[activityFacility updateActivity:activity withMessage:activity.message];
});
nil];
[networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
} else {
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
+ [self syncOperationFinishedWithSuccess:NO];
}
} else {
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
+ [self syncOperationFinishedWithSuccess:NO];
}
} 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];
+ [self syncOperationFinishedWithSuccess:NO];
[pool drain];
return;
}
PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityUpload
message:[NSString stringWithFormat:@"Sync: Uploading '%@' (0%%)", object.name]
totalBytes:[[objectRequest.userInfo objectForKey:@"bytes"] unsignedIntegerValue]
- currentBytes:0];
+ currentBytes:0
+ pithosAccount:pithosAccount];
dispatch_async(dispatch_get_main_queue(), ^{
[activityFacility updateActivity:activity withMessage:activity.message];
});
nil]];
[networkQueue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]];
} else {
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
+ [self syncOperationFinishedWithSuccess:NO];
}
}
[pool drain];
#pragma mark ASIHTTPRequestDelegate
- (void)performRequestFinishedDelegateInBackground:(ASIPithosRequest *)request {
- dispatch_async(queue, ^{
- [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"]) withObject:request];
- });
+ // Add an operation to the callbackQueue with a completionBlock for the case of cancellation
+ NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self
+ selector:NSSelectorFromString([request.userInfo objectForKey:@"didFinishSelector"])
+ object:request] autorelease];
+ operation.completionBlock = ^{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ if ([[request.userInfo objectForKey:@"operation"] isCancelled]) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [activityFacility endActivity:[request.userInfo objectForKey:@"activity"]
+ withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+ });
+ [self syncOperationFinishedWithSuccess:NO];
+ }
+ [pool drain];
+ };
+ [(NSMutableDictionary *)request.userInfo setObject:operation forKey:@"operation"];
+ [callbackQueue addOperation:operation];
}
- (void)performRequestFailedDelegateInBackground:(ASIPithosRequest *)request {
- dispatch_async(queue, ^{
- [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"]) withObject:request];
- });
+ if (request.isCancelled) {
+ // Request has been cancelled
+ // The callbackQueue might be suspended so we call directly the callback method, since it does minimal work anyway
+ [self performSelector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"])
+ withObject:request];
+ } else {
+ // Add an operation to the callbackQueue with a completionBlock for the case of cancellation
+ NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self
+ selector:NSSelectorFromString([request.userInfo objectForKey:@"didFailSelector"])
+ object:request] autorelease];
+ operation.completionBlock = ^{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ if ([[request.userInfo objectForKey:@"operation"] isCancelled]) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [activityFacility endActivity:[request.userInfo objectForKey:@"activity"]
+ withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
+ });
+ [self syncOperationFinishedWithSuccess:NO];
+ }
+ [pool drain];
+ };
+ [(NSMutableDictionary *)request.userInfo setObject:operation forKey:@"operation"];
+ [callbackQueue addOperation:operation];
+ }
}
- (void)listRequestFinished:(ASIPithosContainerRequest *)containerRequest {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"];
NSLog(@"Sync::list request finished: %@", containerRequest.url);
- if ((containerRequest.responseStatusCode == 200) || (containerRequest.responseStatusCode == 304)) {
+ if (operation.isCancelled) {
+ [self listRequestFailed:containerRequest];
+ } else if ((containerRequest.responseStatusCode == 200) || (containerRequest.responseStatusCode == 304)) {
if (containerRequest.responseStatusCode == 200) {
NSArray *someObjects = [containerRequest objects];
if (objects == nil) {
newContainerRequest.delegate = self;
newContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
newContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
- newContainerRequest.userInfo = newContainerRequest.userInfo;
+ newContainerRequest.userInfo = containerRequest.userInfo;
[(NSMutableDictionary *)newContainerRequest.userInfo setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
[networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
[pool drain];
return;
}
}
+
+ if (operation.isCancelled) {
+ [self listRequestFailed:containerRequest];
+ [pool drain];
+ return;
+ }
+
dispatch_async(dispatch_get_main_queue(), ^{
[activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"]
withMessage:[containerRequest.userInfo objectForKey:@"finishedActivityMessage"]];
[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"];
+ [activityFacility startAndEndActivityWithType:PithosActivityOther
+ message:@"Sync: Failed to read contents of sync directory"
+ pithosAccount:pithosAccount];
});
- @synchronized(self) {
- // Since the local listing failed, the operation finished and the sync cycle is completeted unsuccessfully
- syncOperationCount = 0;
- if (newSyncRequested)
- [self sync];
- }
+ // Since the local listing failed, the operation finished and the sync cycle is completeted unsuccessfully
+ [self syncOperationFinishedWithSuccess:NO];
+ [pool drain];
+ return;
+ }
+
+ if (operation.isCancelled) {
+ operation.completionBlock = nil;
+ [self syncOperationFinishedWithSuccess:NO];
[pool drain];
return;
}
+
self.currentLocalObjectStates = [NSMutableDictionary dictionaryWithCapacity:[subPaths count]];
for (NSString *objectName in subPaths) {
+ if (operation.isCancelled) {
+ operation.completionBlock = nil;
+ [self saveLocalState];
+ [self syncOperationFinishedWithSuccess:NO];
+ [pool drain];
+ return;
+ }
+
PithosLocalObjectState *storedLocalObjectState = [storedLocalObjectStates objectForKey:objectName];
NSString *filePath = [containerDirectoryPath stringByAppendingPathComponent:objectName];
if (!storedLocalObjectState || [storedLocalObjectState isModified]) {
}
[self saveLocalState];
+ if (operation.isCancelled) {
+ operation.completionBlock = nil;
+ [self syncOperationFinishedWithSuccess:NO];
+ [pool drain];
+ return;
+ }
+
for (NSString *objectName in remoteObjects) {
+ if (operation.isCancelled) {
+ operation.completionBlock = nil;
+ [self saveLocalState];
+ [self syncOperationFinishedWithSuccess:NO];
+ [pool drain];
+ return;
+ }
+
if (![storedLocalObjectStates objectForKey:objectName])
[storedLocalObjectStates setObject:[PithosLocalObjectState localObjectState] forKey:objectName];
}
+ [self saveLocalState];
+
+ if (operation.isCancelled) {
+ operation.completionBlock = nil;
+ [self syncOperationFinishedWithSuccess:NO];
+ [pool drain];
+ return;
+ }
for (NSString *objectName in [[storedLocalObjectStates allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
+ if (operation.isCancelled) {
+ operation.completionBlock = nil;
+ [self syncOperationFinishedWithSuccess:NO];
+ [pool drain];
+ return;
+ }
+
NSString *filePath = [containerDirectoryPath stringByAppendingPathComponent:objectName];
if ([objectName hasSuffix:@"/"])
filePath = [filePath stringByAppendingString:@":"];
}
}
}
- @synchronized(self) {
- [self decreaseSyncOperationCount];
- if (newSyncRequested && !syncOperationCount)
- [self sync];
- }
+ [self syncOperationFinishedWithSuccess:YES];
} else {
- NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
- if (retries > 0) {
- ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[[PithosUtilities copyRequest:containerRequest] autorelease];
- [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
- [networkQueue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
- } else {
- 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;
- if (newSyncRequested)
- [self sync];
- }
- }
+ [self listRequestFailed:containerRequest];
}
[pool drain];
}
- (void)listRequestFailed:(ASIPithosContainerRequest *)containerRequest {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- if ([containerRequest isCancelled]) {
+ NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"];
+ if (operation.isCancelled) {
+ [objects release];
+ objects = nil;
+ [pool drain];
+ return;
+ }
+ if (containerRequest.isCancelled) {
dispatch_async(dispatch_get_main_queue(), ^{
[activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"]
withMessage:[containerRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
});
[objects release];
objects = nil;
- @synchronized(self) {
- syncOperationCount = 0;
- }
+ [self syncOperationFinishedWithSuccess:NO];
[pool drain];
return;
}
});
[objects release];
objects = nil;
- @synchronized(self) {
- // Since the server listing failed in all retries, the operation finished and the sync cycle is completeted unsuccesfully
- syncOperationCount = 0;
- if (newSyncRequested)
- [self sync];
- }
+ // Since the server listing failed in all retries, the operation finished and the sync cycle is completeted unsuccesfully
+ [self syncOperationFinishedWithSuccess:NO];
}
[pool drain];
}
- (void)downloadObjectBlockFinished:(ASIPithosObjectRequest *)objectRequest {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
NSLog(@"Sync::download object block finished: %@", objectRequest.url);
- if (objectRequest.responseStatusCode == 206) {
+ if (operation.isCancelled) {
+ [self requestFailed:objectRequest];
+ } else if (objectRequest.responseStatusCode == 206) {
ASIPithosObject *object = [objectRequest.userInfo objectForKey:@"pithosObject"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
[activityFacility endActivity:activity
withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
});
- @synchronized(self) {
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
- if (newSyncRequested && !syncOperationCount)
- [self sync];
- }
+ [self syncOperationFinishedWithSuccess:NO];
[pool drain];
return;
}
[activityFacility endActivity:activity
withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
});
- @synchronized(self) {
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
- if (newSyncRequested && !syncOperationCount)
- [self sync];
- }
+ [self syncOperationFinishedWithSuccess:NO];
[pool drain];
return;
}
[activityFacility endActivity:activity
withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
});
- @synchronized(self) {
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
- if (newSyncRequested && !syncOperationCount)
- [self sync];
- }
+ [self syncOperationFinishedWithSuccess:NO];
[pool drain];
return;
} else if (![fileManager fileExistsAtPath:dirPath]) {
[activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
});
- @synchronized(self) {
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
- if (newSyncRequested && !syncOperationCount)
- [self sync];
- }
+ [self syncOperationFinishedWithSuccess:NO];
[pool drain];
return;
}
[activityFacility endActivity:activity
withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
});
- @synchronized(self) {
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
- if (newSyncRequested && !syncOperationCount)
- [self sync];
- }
+ [self syncOperationFinishedWithSuccess:NO];
[pool drain];
return;
}
storedState.hash = object.objectHash;
storedState.tmpFilePath = nil;
[self saveLocalState];
-
- @synchronized(self) {
- [self decreaseSyncOperationCount];
- if (newSyncRequested && !syncOperationCount)
- [self sync];
- }
+ [self syncOperationFinishedWithSuccess:YES];
[pool drain];
return;
} else {
- if (newSyncRequested) {
- dispatch_async(dispatch_get_main_queue(), ^{
- [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
- withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
- });
- @synchronized(self) {
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
- if (!syncOperationCount)
- [self sync];
- }
- [pool drain];
- return;
+ if (newSyncRequested || syncLate || operation.isCancelled) {
+ [self requestFailed:objectRequest];
} else {
__block ASIPithosObjectRequest *newObjectRequest = [PithosUtilities objectBlockDataRequestWithPithos:pithos
containerName:containerName
[activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
});
- @synchronized(self) {
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
- if (newSyncRequested && !syncOperationCount)
- [self sync];
- }
+ [self syncOperationFinishedWithSuccess:NO];
} else {
[self requestFailed:objectRequest];
}
- (void)downloadObjectHashMapFinished:(ASIPithosObjectRequest *)objectRequest {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
NSLog(@"Sync::download object hashmap finished: %@", objectRequest.url);
- if (objectRequest.responseStatusCode == 200) {
- if (newSyncRequested) {
- dispatch_async(dispatch_get_main_queue(), ^{
- [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
- withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
- });
- @synchronized(self) {
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
- if (!syncOperationCount)
- [self sync];
- }
- [pool drain];
- return;
+ if (operation.isCancelled) {
+ [self requestFailed:objectRequest];
+ } else if (objectRequest.responseStatusCode == 200) {
+ if (newSyncRequested || syncLate || operation.isCancelled) {
+ [self requestFailed:objectRequest];
} else {
ASIPithosObject *object = [objectRequest.userInfo objectForKey:@"pithosObject"];
PithosLocalObjectState *storedState = [storedLocalObjectStates objectForKey:object.name];
- (void)uploadDirectoryObjectFinished:(ASIPithosObjectRequest *)objectRequest {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
NSLog(@"Sync::upload directory object finished: %@", objectRequest.url);
- if (objectRequest.responseStatusCode == 201) {
+ if (operation.isCancelled) {
+ [self requestFailed:objectRequest];
+ } else 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"]];
});
- @synchronized(self) {
- [self decreaseSyncOperationCount];
- if (newSyncRequested && !syncOperationCount)
- [self sync];
- }
+ [self syncOperationFinishedWithSuccess:YES];
} else {
[self requestFailed:objectRequest];
}
- (void)moveObjectToTrashFinished:(ASIPithosObjectRequest *)objectRequest {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
NSLog(@"Sync::move object to trash finished: %@", objectRequest.url);
- if (objectRequest.responseStatusCode == 201) {
+ if (operation.isCancelled) {
+ [self requestFailed:objectRequest];
+ } else if (objectRequest.responseStatusCode == 201) {
[storedLocalObjectStates removeObjectForKey:[[objectRequest.userInfo objectForKey:@"pithosObject"] name]];
[self saveLocalState];
dispatch_async(dispatch_get_main_queue(), ^{
[activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"]
withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
});
- @synchronized(self) {
- [self decreaseSyncOperationCount];
- if (newSyncRequested && !syncOperationCount)
- [self sync];
- }
+ [self syncOperationFinishedWithSuccess:YES];
} else {
[self requestFailed:objectRequest];
}
- (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSOperation *operation = [objectRequest.userInfo objectForKey:@"operation"];
NSLog(@"Sync::upload using hashmap finished: %@", objectRequest.url);
ASIPithosObject *object = [objectRequest.userInfo objectForKey:@"pithosObject"];
PithosLocalObjectState *storedState = [storedLocalObjectStates objectForKey:object.name];
PithosActivity *activity = [objectRequest.userInfo objectForKey:@"activity"];
NSUInteger totalBytes = activity.totalBytes;
NSUInteger currentBytes = activity.currentBytes;
- if (objectRequest.responseStatusCode == 201) {
+ if (operation.isCancelled) {
+ [self requestFailed:objectRequest];
+ } else if (objectRequest.responseStatusCode == 201) {
NSLog(@"Sync::object created: %@", objectRequest.url);
storedState.hash = [objectRequest objectHash];
[self saveLocalState];
totalBytes:totalBytes
currentBytes:totalBytes];
});
- @synchronized(self) {
- [self decreaseSyncOperationCount];
- if (newSyncRequested && !syncOperationCount)
- [self sync];
- }
+ [self syncOperationFinishedWithSuccess:YES];
} else if (objectRequest.responseStatusCode == 409) {
- if (newSyncRequested) {
- dispatch_async(dispatch_get_main_queue(), ^{
- [activityFacility endActivity:activity
- withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
- });
- @synchronized(self) {
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
- if (!syncOperationCount)
- [self sync];
- }
- [pool drain];
- return;
+ if (newSyncRequested || syncLate || operation.isCancelled) {
+ [self requestFailed:objectRequest];
} else {
NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue];
if (iteration == 0) {
NSLog(@"Sync::upload iteration limit reached: %@", objectRequest.url);
dispatch_async(dispatch_get_main_queue(), ^{
- [activityFacility endActivity:activity
- withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+ [activityFacility endActivity:activity withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
});
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
+ [self syncOperationFinishedWithSuccess:NO];
[pool drain];
return;
}
- (void)uploadMissingBlockFinished:(ASIPithosContainerRequest *)containerRequest {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSOperation *operation = [containerRequest.userInfo objectForKey:@"operation"];
NSLog(@"Sync::upload of missing block finished: %@", containerRequest.url);
- if (containerRequest.responseStatusCode == 202) {
+ if (operation.isCancelled) {
+ [self requestFailed:containerRequest];
+ } else if (containerRequest.responseStatusCode == 202) {
ASIPithosObject *object = [containerRequest.userInfo objectForKey:@"pithosObject"];
PithosActivity *activity = [containerRequest.userInfo objectForKey:@"activity"];
NSIndexSet *missingBlocks = [containerRequest.userInfo objectForKey:@"missingBlocks"];
NSUInteger missingBlockIndex = [[containerRequest.userInfo objectForKey:@"missingBlockIndex"] unsignedIntegerValue];
missingBlockIndex = [missingBlocks indexGreaterThanIndex:missingBlockIndex];
- if (missingBlockIndex == NSNotFound) {
+ if (operation.isCancelled) {
+ [self requestFailed:containerRequest];
+ } else if (missingBlockIndex == NSNotFound) {
NSArray *hashes = [containerRequest.userInfo objectForKey:@"hashes"];
ASIPithosObjectRequest *newObjectRequest = [PithosUtilities writeObjectDataRequestWithPithos:pithos
containerName:containerName
[(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:NSStringFromSelector(@selector(uploadObjectUsingHashMapFinished:)) forKey:@"didFinishSelector"];
[networkQueue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]];
} else {
- if (newSyncRequested) {
- dispatch_async(dispatch_get_main_queue(), ^{
- [activityFacility endActivity:activity
- withMessage:[containerRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
- });
- @synchronized(self) {
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
- if (!syncOperationCount)
- [self sync];
- }
- [pool drain];
- return;
+ if (newSyncRequested || syncLate || operation.isCancelled) {
+ [self requestFailed:containerRequest];
} else {
__block ASIPithosContainerRequest *newContainerRequest = [PithosUtilities updateContainerDataRequestWithPithos:pithos
containerName:containerName
- (void)requestFailed:(ASIPithosRequest *)request {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- if ([request isCancelled]) {
- dispatch_async(dispatch_get_main_queue(), ^{
- [activityFacility endActivity:[request.userInfo objectForKey:@"activity"]
- withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
- });
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
+ NSOperation *operation = [request.userInfo objectForKey:@"operation"];
+ if (operation.isCancelled) {
[pool drain];
- return;
+ return;
}
- if (newSyncRequested) {
+ if (request.isCancelled || newSyncRequested || syncLate) {
dispatch_async(dispatch_get_main_queue(), ^{
[activityFacility endActivity:[request.userInfo objectForKey:@"activity"]
withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
});
- @synchronized(self) {
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
- if (!syncOperationCount)
- [self sync];
- }
+ [self syncOperationFinishedWithSuccess:NO];
[pool drain];
return;
}
[activityFacility endActivity:[request.userInfo objectForKey:@"activity"]
withMessage:[request.userInfo objectForKey:@"failedActivityMessage"]];
});
- syncIncomplete = YES;
- [self decreaseSyncOperationCount];
+ [self syncOperationFinishedWithSuccess:NO];
}
[pool drain];
}