Flush host when network connectivity changes

pull/8441/head
Muxi Yan 8 years ago
parent a8d6681cce
commit e1443b195e
  1. 40
      src/objective-c/GRPCClient/GRPCCall.m
  2. 3
      src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h
  3. 17
      src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m
  4. 13
      src/objective-c/GRPCClient/private/GRPCHost.m
  5. 1
      src/objective-c/GRPCClient/private/GRPCReachabilityFlagNames.xmacro.h
  6. 2
      src/objective-c/tests/Connectivity/ConnectivityTestingApp.xcodeproj/project.pbxproj

@ -375,20 +375,6 @@ static NSMutableDictionary *callFlags;
_state = GRXWriterStateStarted; _state = GRXWriterStateStarted;
} }
// Create a retain cycle so that this instance lives until the RPC finishes (or is cancelled).
// This makes RPCs in which the call isn't externally retained possible (as long as it is started
// before being autoreleased).
// Care is taken not to retain self strongly in any of the blocks used in this implementation, so
// that the life of the instance is determined by this retain cycle.
_retainSelf = self;
_responseWriteable = [[GRXConcurrentWriteable alloc] initWithWriteable:writeable];
_wrappedCall = [[GRPCWrappedCall alloc] initWithHost:_host path:_path];
NSAssert(_wrappedCall, @"Error allocating RPC objects. Low memory?");
[self sendHeaders:_requestHeaders];
[self invokeCall];
// TODO(jcanizales): Extract this logic somewhere common. // TODO(jcanizales): Extract this logic somewhere common.
NSString *host = [NSURL URLWithString:[@"https://" stringByAppendingString:_host]].host; NSString *host = [NSURL URLWithString:[@"https://" stringByAppendingString:_host]].host;
if (!host) { if (!host) {
@ -397,15 +383,35 @@ static NSMutableDictionary *callFlags;
} }
__weak typeof(self) weakSelf = self; __weak typeof(self) weakSelf = self;
_connectivityMonitor = [GRPCConnectivityMonitor monitorWithHost:host]; _connectivityMonitor = [GRPCConnectivityMonitor monitorWithHost:host];
[_connectivityMonitor handleLossWithHandler:^{ void (^handler)() = ^{
typeof(self) strongSelf = weakSelf; typeof(self) strongSelf = weakSelf;
if (strongSelf) { if (strongSelf) {
[strongSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain [strongSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
code:GRPCErrorCodeUnavailable code:GRPCErrorCodeUnavailable
userInfo:@{NSLocalizedDescriptionKey: @"Connectivity lost."}]]; userInfo:@{NSLocalizedDescriptionKey: @"Connectivity lost."}]];
[[GRPCHost hostWithAddress:strongSelf->_host] disconnect];
} }
}]; };
[_connectivityMonitor handleLossWithHandler:handler
wifiStatusChangeHandler:handler];
// Create a retain cycle so that this instance lives until the RPC finishes
// (or is cancelled).
// This makes RPCs in which the call isn't externally retained possible (as
// long as it is started
// before being autoreleased).
// Care is taken not to retain self strongly in any of the blocks used in this
// implementation, so
// that the life of the instance is determined by this retain cycle.
_retainSelf = self;
_responseWriteable =
[[GRXConcurrentWriteable alloc] initWithWriteable:writeable];
_wrappedCall = [[GRPCWrappedCall alloc] initWithHost:_host path:_path];
NSAssert(_wrappedCall, @"Error allocating RPC objects. Low memory?");
[self sendHeaders:_requestHeaders];
[self invokeCall];
} }
- (void)setState:(GRXWriterState)newState { - (void)setState:(GRXWriterState)newState {

@ -73,5 +73,6 @@
* Only one handler is active at a time, so if this method is called again before the previous * Only one handler is active at a time, so if this method is called again before the previous
* handler has been called, it might never be called at all (or yes, if it has already been queued). * handler has been called, it might never be called at all (or yes, if it has already been queued).
*/ */
- (void)handleLossWithHandler:(nonnull void (^)())handler; - (void)handleLossWithHandler:(void (^)())handler
wifiStatusChangeHandler:(void (^)())wifiStatusChangeHandler;
@end @end

@ -120,6 +120,7 @@ static void PassFlagsToContextInfoBlock(SCNetworkReachabilityRef target,
@implementation GRPCConnectivityMonitor { @implementation GRPCConnectivityMonitor {
SCNetworkReachabilityRef _reachabilityRef; SCNetworkReachabilityRef _reachabilityRef;
GRPCReachabilityFlags *_previousReachabilityFlags;
} }
- (nullable instancetype)initWithReachability:(nullable SCNetworkReachabilityRef)reachability { - (nullable instancetype)initWithReachability:(nullable SCNetworkReachabilityRef)reachability {
@ -129,6 +130,13 @@ static void PassFlagsToContextInfoBlock(SCNetworkReachabilityRef target,
if ((self = [super init])) { if ((self = [super init])) {
_reachabilityRef = CFRetain(reachability); _reachabilityRef = CFRetain(reachability);
_queue = dispatch_get_main_queue(); _queue = dispatch_get_main_queue();
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) {
_previousReachabilityFlags =
[[GRPCReachabilityFlags alloc] initWithFlags:flags];
} else {
_previousReachabilityFlags = 0;
}
} }
return self; return self;
} }
@ -149,11 +157,16 @@ static void PassFlagsToContextInfoBlock(SCNetworkReachabilityRef target,
return returnValue; return returnValue;
} }
- (void)handleLossWithHandler:(void (^)())handler { - (void)handleLossWithHandler:(void (^)())handler
wifiStatusChangeHandler:(nonnull void (^)())wifiStatusChangeHandler {
[self startListeningWithHandler:^(GRPCReachabilityFlags *flags) { [self startListeningWithHandler:^(GRPCReachabilityFlags *flags) {
if (!flags.isHostReachable) { if (!flags.reachable && handler) {
handler(); handler();
} else if (flags.isWWAN ^ _previousReachabilityFlags.isWWAN &&
wifiStatusChangeHandler) {
wifiStatusChangeHandler();
} }
_previousReachabilityFlags = flags;
}]; }];
} }

@ -43,6 +43,7 @@
#import "GRPCChannel.h" #import "GRPCChannel.h"
#import "GRPCCompletionQueue.h" #import "GRPCCompletionQueue.h"
#import "GRPCConnectivityMonitor.h"
#import "NSDictionary+GRPC.h" #import "NSDictionary+GRPC.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@ -52,6 +53,7 @@ NS_ASSUME_NONNULL_BEGIN
#define GRPC_OBJC_VERSION_STRING @"1.0.0" #define GRPC_OBJC_VERSION_STRING @"1.0.0"
static NSMutableDictionary *kHostCache; static NSMutableDictionary *kHostCache;
static GRPCConnectivityMonitor *connectivityMonitor = nil;
@implementation GRPCHost { @implementation GRPCHost {
// TODO(mlumish): Investigate whether caching channels with strong links is a good idea. // TODO(mlumish): Investigate whether caching channels with strong links is a good idea.
@ -98,6 +100,16 @@ static NSMutableDictionary *kHostCache;
_address = address; _address = address;
_secure = YES; _secure = YES;
kHostCache[address] = self; kHostCache[address] = self;
if (!connectivityMonitor) {
connectivityMonitor =
[GRPCConnectivityMonitor monitorWithHost:hostURL.host];
void (^handler)() = ^{
[GRPCHost flushChannelCache];
};
[connectivityMonitor handleLossWithHandler:handler
wifiStatusChangeHandler:handler];
}
} }
} }
return self; return self;
@ -110,6 +122,7 @@ static NSMutableDictionary *kHostCache;
BOOL * _Nonnull stop) { BOOL * _Nonnull stop) {
[host disconnect]; [host disconnect];
}]; }];
connectivityMonitor = nil;
} }
} }

@ -65,3 +65,4 @@ GRPC_XMACRO_ITEM(interventionRequired, InterventionRequired)
GRPC_XMACRO_ITEM(connectionOnDemand, ConnectionOnDemand) GRPC_XMACRO_ITEM(connectionOnDemand, ConnectionOnDemand)
GRPC_XMACRO_ITEM(isLocalAddress, IsLocalAddress) GRPC_XMACRO_ITEM(isLocalAddress, IsLocalAddress)
GRPC_XMACRO_ITEM(isDirect, IsDirect) GRPC_XMACRO_ITEM(isDirect, IsDirect)
GRPC_XMACRO_ITEM(isWWAN, IsWWAN)

@ -156,7 +156,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
5347BF6C41E7888C1C05CD88 /* [CP] Copy Pods Resources */ = { 5347BF6C41E7888C1C05CD88 /* [CP] Copy Pods Resources */ = {

Loading…
Cancel
Save