mirror of
https://github.com/moonlight-stream/moonlight-ios.git
synced 2026-06-17 14:11:35 +00:00
Fix a race condition that could cause an app to be populated with a null host
This commit is contained in:
@@ -28,7 +28,6 @@
|
|||||||
Host* _selectedHost;
|
Host* _selectedHost;
|
||||||
NSString* _uniqueId;
|
NSString* _uniqueId;
|
||||||
NSData* _cert;
|
NSData* _cert;
|
||||||
NSString* _currentGame;
|
|
||||||
DiscoveryManager* _discMan;
|
DiscoveryManager* _discMan;
|
||||||
AppAssetManager* _appManager;
|
AppAssetManager* _appManager;
|
||||||
StreamConfiguration* _streamConfig;
|
StreamConfiguration* _streamConfig;
|
||||||
@@ -72,69 +71,82 @@ static NSMutableSet* hostList;
|
|||||||
|
|
||||||
- (void)alreadyPaired {
|
- (void)alreadyPaired {
|
||||||
BOOL usingCachedAppList = false;
|
BOOL usingCachedAppList = false;
|
||||||
if ([_selectedHost.appList count] > 0) {
|
|
||||||
|
// Capture the host here because it can change once we
|
||||||
|
// leave the main thread
|
||||||
|
Host* host = _selectedHost;
|
||||||
|
|
||||||
|
if ([host.appList count] > 0) {
|
||||||
usingCachedAppList = true;
|
usingCachedAppList = true;
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
_computerNameButton.title = _selectedHost.name;
|
if (host != _selectedHost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_computerNameButton.title = host.name;
|
||||||
[self.navigationController.navigationBar setNeedsLayout];
|
[self.navigationController.navigationBar setNeedsLayout];
|
||||||
[self updateApps];
|
|
||||||
|
[self updateAppsForHost:host];
|
||||||
[self hideLoadingFrame];
|
[self hideLoadingFrame];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Log(LOG_I, @"Using cached app list: %d", usingCachedAppList);
|
Log(LOG_I, @"Using cached app list: %d", usingCachedAppList);
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:_selectedHost.activeAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
HttpManager* hMan = [[HttpManager alloc] initWithHost:host.activeAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||||
|
|
||||||
AppListResponse* appListResp = [[AppListResponse alloc] init];
|
AppListResponse* appListResp = [[AppListResponse alloc] init];
|
||||||
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:appListResp withUrlRequest:[hMan newAppListRequest]]];
|
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:appListResp withUrlRequest:[hMan newAppListRequest]]];
|
||||||
if (appListResp == nil || ![appListResp isStatusOk] || [appListResp getAppList] == nil) {
|
if (appListResp == nil || ![appListResp isStatusOk] || [appListResp getAppList] == nil) {
|
||||||
Log(LOG_W, @"Failed to get applist: %@", appListResp.statusMessage);
|
Log(LOG_W, @"Failed to get applist: %@", appListResp.statusMessage);
|
||||||
[self hideLoadingFrame];
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self hideLoadingFrame];
|
||||||
UIAlertController* applistAlert = [UIAlertController alertControllerWithTitle:@"Fetching App List Failed"
|
UIAlertController* applistAlert = [UIAlertController alertControllerWithTitle:@"Fetching App List Failed"
|
||||||
message:@"The connection to the PC was interrupted."
|
message:@"The connection to the PC was interrupted."
|
||||||
preferredStyle:UIAlertControllerStyleAlert];
|
preferredStyle:UIAlertControllerStyleAlert];
|
||||||
[applistAlert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:nil]];
|
[applistAlert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:nil]];
|
||||||
[self presentViewController:applistAlert animated:YES completion:nil];
|
[self presentViewController:applistAlert animated:YES completion:nil];
|
||||||
_selectedHost.online = NO;
|
host.online = NO;
|
||||||
[self showHostSelectionView];
|
[self showHostSelectionView];
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
[self mergeAppLists:[appListResp getAppList]];
|
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
_computerNameButton.title = _selectedHost.name;
|
[self mergeAppLists:[appListResp getAppList] forHost:host];
|
||||||
|
|
||||||
|
if (host != _selectedHost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_computerNameButton.title = host.name;
|
||||||
[self.navigationController.navigationBar setNeedsLayout];
|
[self.navigationController.navigationBar setNeedsLayout];
|
||||||
|
|
||||||
[self updateApps];
|
[self updateAppsForHost:host];
|
||||||
|
[_appManager stopRetrieving];
|
||||||
|
[_appManager retrieveAssetsFromHost:host];
|
||||||
|
[self hideLoadingFrame];
|
||||||
});
|
});
|
||||||
|
|
||||||
[_appManager stopRetrieving];
|
|
||||||
[_appManager retrieveAssetsFromHost:_selectedHost];
|
|
||||||
[self hideLoadingFrame];
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) mergeAppLists:(NSArray*) newList {
|
- (void) mergeAppLists:(NSArray*) newList forHost:(Host*)host {
|
||||||
DataManager* database = [[DataManager alloc] init];
|
DataManager* database = [[DataManager alloc] init];
|
||||||
for (App* app in newList) {
|
for (App* app in newList) {
|
||||||
BOOL appAlreadyInList = NO;
|
BOOL appAlreadyInList = NO;
|
||||||
for (App* savedApp in _selectedHost.appList) {
|
for (App* savedApp in host.appList) {
|
||||||
if ([app.id isEqualToString:savedApp.id]) {
|
if ([app.id isEqualToString:savedApp.id]) {
|
||||||
appAlreadyInList = YES;
|
appAlreadyInList = YES;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!appAlreadyInList) {
|
if (!appAlreadyInList) {
|
||||||
app.host = _selectedHost;
|
app.host = host;
|
||||||
[_selectedHost addAppListObject:app];
|
[host addAppListObject:app];
|
||||||
} else {
|
} else {
|
||||||
[database removeApp:app];
|
[database removeApp:app];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (App* app in _selectedHost.appList) {
|
for (App* app in host.appList) {
|
||||||
BOOL appWasRemoved = YES;
|
BOOL appWasRemoved = YES;
|
||||||
for (App* mergedApp in newList) {
|
for (App* mergedApp in newList) {
|
||||||
if ([mergedApp.id isEqualToString:app.id]) {
|
if ([mergedApp.id isEqualToString:app.id]) {
|
||||||
@@ -143,7 +155,7 @@ static NSMutableSet* hostList;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (appWasRemoved) {
|
if (appWasRemoved) {
|
||||||
[_selectedHost removeAppListObject:app];
|
[host removeAppListObject:app];
|
||||||
[database removeApp:app];
|
[database removeApp:app];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,7 +172,9 @@ static NSMutableSet* hostList;
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void) receivedAssetForApp:(App*)app {
|
- (void) receivedAssetForApp:(App*)app {
|
||||||
[self.collectionView reloadData];
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self.collectionView reloadData];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)displayDnsFailedDialog {
|
- (void)displayDnsFailedDialog {
|
||||||
@@ -183,14 +197,13 @@ static NSMutableSet* hostList;
|
|||||||
|
|
||||||
Log(LOG_D, @"Clicked host: %@", host.name);
|
Log(LOG_D, @"Clicked host: %@", host.name);
|
||||||
_selectedHost = host;
|
_selectedHost = host;
|
||||||
|
[self disableNavigation];
|
||||||
|
|
||||||
// If we are online, paired, and have a cached app list, skip straight
|
// If we are online, paired, and have a cached app list, skip straight
|
||||||
// to the app grid without a loading frame. This is the fast path that users
|
// to the app grid without a loading frame. This is the fast path that users
|
||||||
// should hit most.
|
// should hit most.
|
||||||
if (host.online && host.pairState == PairStatePaired && host.appList.count > 0) {
|
if (host.online && host.pairState == PairStatePaired && host.appList.count > 0) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
[self alreadyPaired];
|
||||||
[self alreadyPaired];
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,8 +215,8 @@ static NSMutableSet* hostList;
|
|||||||
fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]];
|
fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]];
|
||||||
if (serverInfoResp == nil || ![serverInfoResp isStatusOk]) {
|
if (serverInfoResp == nil || ![serverInfoResp isStatusOk]) {
|
||||||
Log(LOG_W, @"Failed to get server info: %@", serverInfoResp.statusMessage);
|
Log(LOG_W, @"Failed to get server info: %@", serverInfoResp.statusMessage);
|
||||||
[self hideLoadingFrame];
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self hideLoadingFrame];
|
||||||
UIAlertController* applistAlert = [UIAlertController alertControllerWithTitle:@"Fetching Server Info Failed"
|
UIAlertController* applistAlert = [UIAlertController alertControllerWithTitle:@"Fetching Server Info Failed"
|
||||||
message:@"The connection to the PC was interrupted."
|
message:@"The connection to the PC was interrupted."
|
||||||
preferredStyle:UIAlertControllerStyleAlert];
|
preferredStyle:UIAlertControllerStyleAlert];
|
||||||
@@ -304,7 +317,7 @@ static NSMutableSet* hostList;
|
|||||||
- (void) appClicked:(App *)app {
|
- (void) appClicked:(App *)app {
|
||||||
Log(LOG_D, @"Clicked app: %@", app.name);
|
Log(LOG_D, @"Clicked app: %@", app.name);
|
||||||
_streamConfig = [[StreamConfiguration alloc] init];
|
_streamConfig = [[StreamConfiguration alloc] init];
|
||||||
_streamConfig.host = _selectedHost.activeAddress;
|
_streamConfig.host = app.host.activeAddress;
|
||||||
_streamConfig.appID = app.id;
|
_streamConfig.appID = app.id;
|
||||||
|
|
||||||
DataManager* dataMan = [[DataManager alloc] init];
|
DataManager* dataMan = [[DataManager alloc] init];
|
||||||
@@ -321,7 +334,7 @@ static NSMutableSet* hostList;
|
|||||||
[[self revealViewController] revealToggle:self];
|
[[self revealViewController] revealToggle:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
App* currentApp = [self findRunningApp];
|
App* currentApp = [self findRunningApp:app.host];
|
||||||
if (currentApp != nil) {
|
if (currentApp != nil) {
|
||||||
UIAlertController* alertController = [UIAlertController
|
UIAlertController* alertController = [UIAlertController
|
||||||
alertControllerWithTitle: app.name
|
alertControllerWithTitle: app.name
|
||||||
@@ -335,7 +348,7 @@ static NSMutableSet* hostList;
|
|||||||
[app.id isEqualToString:currentApp.id] ? @"Quit App" : @"Quit Running App and Start" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){
|
[app.id isEqualToString:currentApp.id] ? @"Quit App" : @"Quit Running App and Start" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){
|
||||||
Log(LOG_I, @"Quitting application: %@", currentApp.name);
|
Log(LOG_I, @"Quitting application: %@", currentApp.name);
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:_selectedHost.activeAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
HttpManager* hMan = [[HttpManager alloc] initWithHost:app.host.activeAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||||
HttpResponse* quitResponse = [[HttpResponse alloc] init];
|
HttpResponse* quitResponse = [[HttpResponse alloc] init];
|
||||||
HttpRequest* quitRequest = [HttpRequest requestForResponse: quitResponse withUrlRequest:[hMan newQuitAppRequest]];
|
HttpRequest* quitRequest = [HttpRequest requestForResponse: quitResponse withUrlRequest:[hMan newQuitAppRequest]];
|
||||||
[hMan executeRequestSynchronously:quitRequest];
|
[hMan executeRequestSynchronously:quitRequest];
|
||||||
@@ -354,7 +367,7 @@ static NSMutableSet* hostList;
|
|||||||
currentApp.isRunning = NO;
|
currentApp.isRunning = NO;
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self updateApps];
|
[self updateAppsForHost:app.host];
|
||||||
[self performSegueWithIdentifier:@"createStreamFrame" sender:nil];
|
[self performSegueWithIdentifier:@"createStreamFrame" sender:nil];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -371,7 +384,7 @@ static NSMutableSet* hostList;
|
|||||||
|
|
||||||
[alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:nil]];
|
[alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:nil]];
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self updateApps];
|
[self updateAppsForHost:app.host];
|
||||||
[self presentViewController:alert animated:YES completion:nil];
|
[self presentViewController:alert animated:YES completion:nil];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -383,8 +396,8 @@ static NSMutableSet* hostList;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (App*) findRunningApp {
|
- (App*) findRunningApp:(Host*)host {
|
||||||
for (App* app in _selectedHost.appList) {
|
for (App* app in host.appList) {
|
||||||
if (app.isRunning) {
|
if (app.isRunning) {
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
@@ -414,6 +427,7 @@ static NSMutableSet* hostList;
|
|||||||
|
|
||||||
- (void) hideLoadingFrame {
|
- (void) hideLoadingFrame {
|
||||||
[self dismissViewControllerAnimated:YES completion:nil];
|
[self dismissViewControllerAnimated:YES completion:nil];
|
||||||
|
[self enableNavigation];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewDidLoad
|
- (void)viewDidLoad
|
||||||
@@ -554,8 +568,13 @@ static NSMutableSet* hostList;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) updateApps {
|
- (void) updateAppsForHost:(Host*)host {
|
||||||
_sortedAppList = [_selectedHost.appList allObjects];
|
if (host != _selectedHost) {
|
||||||
|
Log(LOG_W, @"Mismatched host during app update");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sortedAppList = [host.appList allObjects];
|
||||||
_sortedAppList = [_sortedAppList sortedArrayUsingSelector:@selector(compareName:)];
|
_sortedAppList = [_sortedAppList sortedArrayUsingSelector:@selector(compareName:)];
|
||||||
|
|
||||||
[hostScrollView removeFromSuperview];
|
[hostScrollView removeFromSuperview];
|
||||||
@@ -613,4 +632,12 @@ static NSMutableSet* hostList;
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) disableNavigation {
|
||||||
|
self.navigationController.navigationBar.topItem.rightBarButtonItem.enabled = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) enableNavigation {
|
||||||
|
self.navigationController.navigationBar.topItem.rightBarButtonItem.enabled = YES;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
Reference in New Issue
Block a user