Use a temporary app object and database lock to avoid saving while the database is not in a consistent state

This commit is contained in:
Cameron Gutman
2015-09-30 20:34:52 -07:00
parent 695499dea9
commit b6445295a7
7 changed files with 101 additions and 28 deletions
+3 -2
View File
@@ -11,6 +11,7 @@
#import "AppDelegate.h"
#import "Host.h"
#import "App.h"
#import "TemporaryApp.h"
@interface DataManager : NSObject
@@ -22,7 +23,7 @@
- (void) saveData;
- (Host*) createHost;
- (void) removeHost:(Host*)host;
- (App*) createApp;
- (void) removeApp:(App*)app;
- (App*) addAppFromTemporaryApp:(TemporaryApp*)tempApp;
- (void) removeAppFromHost:(App*)app;
@end
+53 -17
View File
@@ -7,9 +7,18 @@
//
#import "DataManager.h"
#import "TemporaryApp.h"
@implementation DataManager
+ (NSObject *)databaseLock {
static NSObject *lock = nil;
if (lock == nil) {
lock = [[NSObject alloc] init];
}
return lock;
}
- (id) init {
self = [super init];
self.appDelegate = [[UIApplication sharedApplication] delegate];
@@ -57,38 +66,65 @@
}
- (void) saveData {
NSError* error;
if (![[self.appDelegate managedObjectContext] save:&error]) {
Log(LOG_E, @"Unable to save hosts to database: %@", error);
@synchronized([DataManager databaseLock]) {
NSError* error;
if (![[self.appDelegate managedObjectContext] save:&error]) {
Log(LOG_E, @"Unable to save hosts to database: %@", error);
}
[self.appDelegate saveContext];
}
[self.appDelegate saveContext];
}
- (NSArray*) retrieveHosts {
return [self fetchRecords:@"Host"];
}
- (App*) createApp {
NSEntityDescription* entity = [NSEntityDescription entityForName:@"App" inManagedObjectContext:[self.appDelegate managedObjectContext]];
return [[App alloc] initWithEntity:entity insertIntoManagedObjectContext:[self.appDelegate managedObjectContext]];
- (App*) addAppFromTemporaryApp:(TemporaryApp*)tempApp {
App* managedApp;
@synchronized([DataManager databaseLock]) {
NSEntityDescription* entity = [NSEntityDescription entityForName:@"App" inManagedObjectContext:[self.appDelegate managedObjectContext]];
managedApp = [[App alloc] initWithEntity:entity insertIntoManagedObjectContext:[self.appDelegate managedObjectContext]];
assert(tempApp.host != nil);
managedApp.id = tempApp.id;
managedApp.image = tempApp.image;
managedApp.name = tempApp.name;
managedApp.isRunning = tempApp.isRunning;
managedApp.host = tempApp.host;
[managedApp.host addAppListObject:managedApp];
}
return managedApp;
}
- (void) removeApp:(App*)app {
[[self.appDelegate managedObjectContext] deleteObject:app];
- (void) removeAppFromHost:(App*)app {
@synchronized([DataManager databaseLock]) {
assert(app.host != nil);
[app.host removeAppListObject:app];
[[self.appDelegate managedObjectContext] deleteObject:app];
}
}
- (NSArray*) fetchRecords:(NSString*)entityName {
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription* entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:[self.appDelegate managedObjectContext]];
[fetchRequest setEntity:entity];
[fetchRequest setAffectedStores:[NSArray arrayWithObjects:[[self.appDelegate persistentStoreCoordinator] persistentStoreForURL:[self.appDelegate getStoreURL]], nil]];
NSArray* fetchedRecords;
NSError* error;
NSArray* fetchedRecords = [[self.appDelegate managedObjectContext] executeFetchRequest:fetchRequest error:&error];
//TODO: handle errors
@synchronized([DataManager databaseLock]) {
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription* entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:[self.appDelegate managedObjectContext]];
[fetchRequest setEntity:entity];
[fetchRequest setAffectedStores:[NSArray arrayWithObjects:[[self.appDelegate persistentStoreCoordinator] persistentStoreForURL:[self.appDelegate getStoreURL]], nil]];
NSError* error;
fetchedRecords = [[self.appDelegate managedObjectContext] executeFetchRequest:fetchRequest error:&error];
//TODO: handle errors
}
return fetchedRecords;
}
@end
+20
View File
@@ -0,0 +1,20 @@
//
// TemporaryApp.h
// Moonlight
//
// Created by Cameron Gutman on 9/30/15.
// Copyright © 2015 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Host.h"
@interface TemporaryApp : NSObject
@property (nullable, nonatomic, retain) NSString *id;
@property (nullable, nonatomic, retain) NSData *image;
@property (nullable, nonatomic, retain) NSString *name;
@property (nonatomic) BOOL isRunning;
@property (nullable, nonatomic, retain) Host *host;
@end
+13
View File
@@ -0,0 +1,13 @@
//
// TemporaryApp.m
// Moonlight
//
// Created by Cameron Gutman on 9/30/15.
// Copyright © 2015 Moonlight Stream. All rights reserved.
//
#import "TemporaryApp.h"
@implementation TemporaryApp
@end
+2 -3
View File
@@ -7,7 +7,7 @@
//
#import "AppListResponse.h"
#import "App.h"
#import "TemporaryApp.h"
#import "DataManager.h"
#import <libxml2/libxml/xmlreader.h>
@@ -60,7 +60,6 @@ static const char* TAG_APP_IS_RUNNING = "IsRunning";
self.statusMessage = statusMsg;
node = node->children;
DataManager* dataMan = [[DataManager alloc] init];
while (node != NULL) {
//Log(LOG_D, @"node: %s", node->name);
@@ -96,7 +95,7 @@ static const char* TAG_APP_IS_RUNNING = "IsRunning";
appInfoNode = appInfoNode->next;
}
if (appId != nil) {
App* app = [dataMan createApp];
TemporaryApp* app = [[TemporaryApp alloc] init];
app.name = appName;
app.id = appId;
app.isRunning = appIsRunning;
@@ -23,6 +23,7 @@
#import "StreamFrameViewController.h"
#import "LoadingFrameViewController.h"
#import "ComputerScrollView.h"
#import "TemporaryApp.h"
@implementation MainFrameViewController {
NSOperationQueue* _opQueue;
@@ -141,7 +142,7 @@ static NSMutableSet* hostList;
- (void) mergeAppLists:(NSArray*) newList forHost:(Host*)host {
DataManager* database = [[DataManager alloc] init];
for (App* app in newList) {
for (TemporaryApp* app in newList) {
BOOL appAlreadyInList = NO;
for (App* savedApp in host.appList) {
if ([app.id isEqualToString:savedApp.id]) {
@@ -152,9 +153,7 @@ static NSMutableSet* hostList;
}
if (!appAlreadyInList) {
app.host = host;
[host addAppListObject:app];
} else {
[database removeApp:app];
[database addAppFromTemporaryApp:app];
}
}
@@ -167,8 +166,7 @@ static NSMutableSet* hostList;
}
}
if (appWasRemoved) {
[host removeAppListObject:app];
[database removeApp:app];
[database removeAppFromHost:app];
}
}
[database saveData];