|
@@ -109,17 +109,19 @@ static void utm_migration_pass_handler(int64_t pass, void *ctx) {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+typedef void(^rpcCompletionHandler_t)(NSDictionary *, NSError *);
|
|
|
|
+
|
|
@interface UTMQemuManager ()
|
|
@interface UTMQemuManager ()
|
|
|
|
|
|
@property (nonatomic, readwrite) BOOL isConnected;
|
|
@property (nonatomic, readwrite) BOOL isConnected;
|
|
@property (nonatomic, readwrite) NSInteger retries;
|
|
@property (nonatomic, readwrite) NSInteger retries;
|
|
@property (nonatomic, nullable) qemuManagerCompletionHandler_t completionCallback;
|
|
@property (nonatomic, nullable) qemuManagerCompletionHandler_t completionCallback;
|
|
|
|
+@property (nonatomic, nullable) rpcCompletionHandler_t rpcCallback;
|
|
|
|
|
|
@end
|
|
@end
|
|
|
|
|
|
@implementation UTMQemuManager {
|
|
@implementation UTMQemuManager {
|
|
UTMJSONStream *_jsonStream;
|
|
UTMJSONStream *_jsonStream;
|
|
- void (^_rpc_finish)(NSDictionary *, NSError *);
|
|
|
|
dispatch_semaphore_t _cmd_lock;
|
|
dispatch_semaphore_t _cmd_lock;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -128,20 +130,24 @@ void qmp_rpc_call(CFDictionaryRef args, CFDictionaryRef *ret, Error **err, void
|
|
dispatch_semaphore_t rpc_sema = dispatch_semaphore_create(0);
|
|
dispatch_semaphore_t rpc_sema = dispatch_semaphore_create(0);
|
|
__block NSDictionary *dict;
|
|
__block NSDictionary *dict;
|
|
__block NSError *nserr;
|
|
__block NSError *nserr;
|
|
|
|
+ __weak typeof(self) _self = self;
|
|
dispatch_semaphore_wait(self->_cmd_lock, DISPATCH_TIME_FOREVER);
|
|
dispatch_semaphore_wait(self->_cmd_lock, DISPATCH_TIME_FOREVER);
|
|
- self->_rpc_finish = ^(NSDictionary *ret_dict, NSError *ret_err){
|
|
|
|
|
|
+ self.rpcCallback = ^(NSDictionary *ret_dict, NSError *ret_err){
|
|
dispatch_semaphore_t rpc_sema_copy = rpc_sema;
|
|
dispatch_semaphore_t rpc_sema_copy = rpc_sema;
|
|
NSCAssert(ret_dict || ret_err, @"Both dict and err are null");
|
|
NSCAssert(ret_dict || ret_err, @"Both dict and err are null");
|
|
nserr = ret_err;
|
|
nserr = ret_err;
|
|
dict = ret_dict;
|
|
dict = ret_dict;
|
|
- self->_rpc_finish = nil;
|
|
|
|
|
|
+ _self.rpcCallback = nil;
|
|
dispatch_semaphore_signal(rpc_sema_copy); // copy to avoid race condition
|
|
dispatch_semaphore_signal(rpc_sema_copy); // copy to avoid race condition
|
|
};
|
|
};
|
|
- if (![self.jsonStream sendDictionary:(__bridge NSDictionary *)args]) {
|
|
|
|
- self->_rpc_finish(nil, [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"No connection for RPC.", "UTMQemuManager")}]);
|
|
|
|
|
|
+ if (![self.jsonStream sendDictionary:(__bridge NSDictionary *)args] && self.rpcCallback) {
|
|
|
|
+ self.rpcCallback(nil, [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"No connection for RPC.", "UTMQemuManager")}]);
|
|
}
|
|
}
|
|
if (dispatch_semaphore_wait(rpc_sema, dispatch_time(DISPATCH_TIME_NOW, kRPCTimeout)) != 0) {
|
|
if (dispatch_semaphore_wait(rpc_sema, dispatch_time(DISPATCH_TIME_NOW, kRPCTimeout)) != 0) {
|
|
- self->_rpc_finish = nil;
|
|
|
|
|
|
+ // possible race between this timeout and the callback being triggered
|
|
|
|
+ self.rpcCallback = ^(NSDictionary *ret_dict, NSError *ret_err){
|
|
|
|
+ _self.rpcCallback = nil;
|
|
|
|
+ };
|
|
nserr = [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Timed out waiting for RPC.", "UTMQemuManager")}];
|
|
nserr = [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Timed out waiting for RPC.", "UTMQemuManager")}];
|
|
}
|
|
}
|
|
if (ret) {
|
|
if (ret) {
|
|
@@ -168,8 +174,8 @@ void qmp_rpc_call(CFDictionaryRef args, CFDictionaryRef *ret, Error **err, void
|
|
}
|
|
}
|
|
|
|
|
|
- (void)dealloc {
|
|
- (void)dealloc {
|
|
- if (_rpc_finish) {
|
|
|
|
- _rpc_finish(nil, [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Manager being deallocated, killing pending RPC.", "UTMQemuManager")}]);
|
|
|
|
|
|
+ if (self.rpcCallback) {
|
|
|
|
+ self.rpcCallback(nil, [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Manager being deallocated, killing pending RPC.", "UTMQemuManager")}]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -195,8 +201,8 @@ void qmp_rpc_call(CFDictionaryRef args, CFDictionaryRef *ret, Error **err, void
|
|
|
|
|
|
- (void)jsonStream:(UTMJSONStream *)stream seenError:(NSError *)error {
|
|
- (void)jsonStream:(UTMJSONStream *)stream seenError:(NSError *)error {
|
|
UTMLog(@"QMP stream error seen: %@", error);
|
|
UTMLog(@"QMP stream error seen: %@", error);
|
|
- if (_rpc_finish) {
|
|
|
|
- _rpc_finish(nil, error);
|
|
|
|
|
|
+ if (self.rpcCallback) {
|
|
|
|
+ self.rpcCallback(nil, error);
|
|
}
|
|
}
|
|
[self disconnect];
|
|
[self disconnect];
|
|
if (!self.isConnected && self.retries > 0) {
|
|
if (!self.isConnected && self.retries > 0) {
|
|
@@ -214,15 +220,15 @@ void qmp_rpc_call(CFDictionaryRef args, CFDictionaryRef *ret, Error **err, void
|
|
- (void)jsonStream:(UTMJSONStream *)stream receivedDictionary:(NSDictionary *)dict {
|
|
- (void)jsonStream:(UTMJSONStream *)stream receivedDictionary:(NSDictionary *)dict {
|
|
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id val, BOOL *stop) {
|
|
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id val, BOOL *stop) {
|
|
if ([key isEqualToString:@"return"]) {
|
|
if ([key isEqualToString:@"return"]) {
|
|
- if (self->_rpc_finish) {
|
|
|
|
- self->_rpc_finish(dict, nil);
|
|
|
|
|
|
+ if (self.rpcCallback) {
|
|
|
|
+ self.rpcCallback(dict, nil);
|
|
} else {
|
|
} else {
|
|
UTMLog(@"Got unexpected 'return' response: %@", dict);
|
|
UTMLog(@"Got unexpected 'return' response: %@", dict);
|
|
}
|
|
}
|
|
*stop = YES;
|
|
*stop = YES;
|
|
} else if ([key isEqualToString:@"error"]) {
|
|
} else if ([key isEqualToString:@"error"]) {
|
|
- if (self->_rpc_finish) {
|
|
|
|
- self->_rpc_finish(nil, [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: dict[@"error"][@"desc"]}]);
|
|
|
|
|
|
+ if (self.rpcCallback) {
|
|
|
|
+ self.rpcCallback(nil, [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: dict[@"error"][@"desc"]}]);
|
|
} else {
|
|
} else {
|
|
UTMLog(@"Got unexpected 'error' response: %@", dict);
|
|
UTMLog(@"Got unexpected 'error' response: %@", dict);
|
|
}
|
|
}
|