mirror of
https://github.com/OpenEmu/OpenEmuKit.git
synced 2025-11-01 11:08:14 +00:00
feat: Add async / await compatible completion handler
See https://developer.apple.com/documentation/swift/calling_objective-c_apis_asynchronously
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user