feat: Add async / await compatible completion handler

See https://developer.apple.com/documentation/swift/calling_objective-c_apis_asynchronously
This commit is contained in:
Stuart Carnie
2021-10-23 08:24:13 +11:00
parent 8a7d6c2ec6
commit e159d013c7
5 changed files with 143 additions and 1 deletions
+1 -1
View File
@@ -94,7 +94,7 @@ int xpc_task_key = 0;
}];
#ifdef DEBUG_PRINT
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
#else
dispatch_time_t waitTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC * 2));
if (dispatch_semaphore_wait(sem, waitTime) != 0)
+3
View File
@@ -31,6 +31,8 @@
@protocol OEGameCoreOwner;
@class OECorePlugin, OEGameCoreController, OESystemPlugin, OEGameStartupInfo;
typedef void(^OEStartupCompletionHandler)(NSError * _Nullable);
NS_ASSUME_NONNULL_BEGIN
extern NSString * const OEGameCoreErrorDomain;
@@ -60,6 +62,7 @@ typedef NS_ERROR_ENUM(OEGameCoreErrorDomain, OEGameCoreManagerErrorCodes)
#pragma mark - Abstract methods, must be overrode in subclasses
- (void)loadROMWithCompletionHandler:(void(^)(void))completionHandler errorHandler:(void(^)(NSError *))errorHandler;
- (void)loadROMWithCompletionHandler:(OEStartupCompletionHandler)completionHandler;
@end
+5
View File
@@ -60,6 +60,11 @@ NSString * const OEGameCoreErrorDomain = @"OEGameCoreErrorDomain";
[self doesNotImplementSelector:_cmd];
}
- (void)loadROMWithCompletionHandler:(OEStartupCompletionHandler)completionHandler
{
[self doesNotImplementSelector:_cmd];
}
- (void)loadROMWithCompletionHandler:(void(^)(void))completionHandler errorHandler:(void(^)(NSError *))errorHandler
{
[self doesNotImplementSelector:_cmd];
+50
View File
@@ -81,6 +81,20 @@
[_helperThread start];
}
- (void)loadROMWithCompletionHandler:(OEStartupCompletionHandler)completionHandler
{
_helperThread = [[NSThread alloc] initWithTarget:self selector:@selector(_executionThreadNew:) object:completionHandler];
_helperThread.name = @"org.openemu.core-manager-thread";
_helperThread.qualityOfService = NSQualityOfServiceUserInitiated;
_helper = [[OpenEmuHelperApp alloc] init];
_helperProxy = [OEThreadProxy threadProxyWithTarget:_helper thread:_helperThread];
_gameCoreOwnerProxy = [OEThreadProxy threadProxyWithTarget:[self gameCoreOwner] thread:[NSThread mainThread]];
[_helperThread start];
}
- (void)_executionThread:(OEThreadStartup *)startup
{
@autoreleasepool
@@ -122,6 +136,42 @@
[self _notifyGameCoreDidTerminate];
}
- (void)_executionThreadNew:(OEStartupCompletionHandler)handler
{
@autoreleasepool
{
[self setGameCoreHelper:(id<OEGameCoreHelper>)_helperProxy];
[_helper setGameCoreOwner:(id<OEGameCoreOwner>)_gameCoreOwnerProxy];
NSError *error;
if(![_helper loadWithStartupInfo:self.startupInfo error:&error])
{
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
handler(error);
});
return;
}
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
handler(nil);
});
handler = nil;
_dummyTimer = [NSTimer scheduledTimerWithTimeInterval:1e9 repeats:YES block:^(NSTimer * _Nonnull timer) {}];
CFRunLoopRun();
if(_stopHandler)
{
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, _stopHandler);
_stopHandler = nil;
}
}
[self _notifyGameCoreDidTerminate];
}
- (void)_stopHelperThread:(id)object
{
[_dummyTimer invalidate];
+84
View File
@@ -63,6 +63,90 @@
return [NSBundle.mainBundle URLForAuxiliaryExecutable:name];
}
- (void)loadROMWithCompletionHandler:(OEStartupCompletionHandler)completionHandler
{
NSError *error = nil;
_helperConnection = [NSXPCConnection connectionWithServiceName:self.serviceName executableURL:self.executableURL error:&error];
if(_helperConnection == nil)
{
os_log_error(OE_LOG_HELPER, "No listener endpoint for identifier: %@", self.executableURL);
if (error == nil)
{
error = [NSError errorWithDomain:OEGameCoreErrorDomain
code:OEGameCoreCouldNotLoadROMError
userInfo:nil];
}
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
completionHandler(error);
});
// There's no listener endpoint, so don't bother trying to create an NSXPCConnection.
// Returning now since calling initWithListenerEndpoint: and passing it nil results in a memory leak.
// Also, there's no point in trying to get the gameCoreHelper if there's no _helperConnection.
return;
}
__weak OEXPCGameCoreManager *weakSelf = self;
[_helperConnection setInvalidationHandler:^{
OEXPCGameCoreManager *strongSelf = weakSelf;
if (strongSelf)
[strongSelf _notifyGameCoreDidTerminate];
}];
_gameCoreOwnerProxy = [OEThreadProxy threadProxyWithTarget:[self gameCoreOwner] thread:[NSThread mainThread]];
[_helperConnection setExportedInterface:[NSXPCInterface interfaceWithProtocol:@protocol(OEGameCoreOwner)]];
[_helperConnection setExportedObject:_gameCoreOwnerProxy];
NSXPCInterface *intf = [NSXPCInterface interfaceWithProtocol:@protocol(OEXPCGameCoreHelper)];
// startup
[intf setClasses:[NSSet setWithObject:OEGameStartupInfo.class]
forSelector:@selector(loadWithStartupInfo:completionHandler:)
argumentIndex:0
ofReply:NO];
[_helperConnection setRemoteObjectInterface:intf];
[_helperConnection resume];
__block void *gameCoreHelperPointer;
id<OEXPCGameCoreHelper> gameCoreHelper =
[_helperConnection remoteObjectProxyWithErrorHandler:
^(NSError *error)
{
os_log_error(OE_LOG_HELPER, "Helper Connection (%p) failed with error: %{public}@",
gameCoreHelperPointer, error);
[self stop];
}];
gameCoreHelperPointer = (__bridge void *)gameCoreHelper;
if(gameCoreHelper == nil) return;
[gameCoreHelper loadWithStartupInfo:self.startupInfo completionHandler:
^(NSError *error)
{
if(error != nil)
{
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
completionHandler(error);
[self stop];
});
// There's no listener endpoint, so don't bother trying to create an NSXPCConnection.
// Returning now since calling initWithListenerEndpoint: and passing it nil results in a memory leak.
return;
}
[self setGameCoreHelper:gameCoreHelper];
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
completionHandler(nil);
});
}];
}
- (void)loadROMWithCompletionHandler:(void(^)(void))completionHandler errorHandler:(void(^)(NSError *))errorHandler
{
NSError *error = nil;