From 35f003b24eb26c1e51faf2ba8df4ab5784d17133 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Fri, 17 Jul 2015 21:14:36 -0700 Subject: Rename GRPCDelegateWrapper -> GRXConcurrentWriteable And move it to the RxLibrary. --- src/objective-c/GRPCClient/GRPCCall.m | 8 +- .../GRPCClient/private/GRPCDelegateWrapper.h | 81 -------------- .../GRPCClient/private/GRPCDelegateWrapper.m | 120 --------------------- src/objective-c/RxLibrary/GRXConcurrentWriteable.h | 81 ++++++++++++++ src/objective-c/RxLibrary/GRXConcurrentWriteable.m | 120 +++++++++++++++++++++ 5 files changed, 205 insertions(+), 205 deletions(-) delete mode 100644 src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h delete mode 100644 src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m create mode 100644 src/objective-c/RxLibrary/GRXConcurrentWriteable.h create mode 100644 src/objective-c/RxLibrary/GRXConcurrentWriteable.m (limited to 'src/objective-c') diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index 53e5abe177..c41ea41213 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -35,10 +35,10 @@ #include #include +#import #import "private/GRPCChannel.h" #import "private/GRPCCompletionQueue.h" -#import "private/GRPCDelegateWrapper.h" #import "private/GRPCWrappedCall.h" #import "private/NSData+GRPC.h" #import "private/NSDictionary+GRPC.h" @@ -78,7 +78,7 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey"; // do. Particularly, in the face of errors, there's no ordering guarantee at // all. This wrapper over our actual writeable ensures thread-safety and // correct ordering. - GRPCDelegateWrapper *_responseWriteable; + GRXConcurrentWriteable *_responseWriteable; GRXWriter *_requestWriter; NSMutableDictionary *_requestMetadata; @@ -191,7 +191,7 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey"; return; } __weak GRPCCall *weakSelf = self; - __weak GRPCDelegateWrapper *weakWriteable = _responseWriteable; + __weak GRXConcurrentWriteable *weakWriteable = _responseWriteable; dispatch_async(_callQueue, ^{ [weakSelf startReadWithHandler:^(grpc_byte_buffer *message) { @@ -340,7 +340,7 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey"; // Care is taken not to retain self strongly in any of the blocks used in // the implementation of GRPCCall, so that the life of the instance is // determined by this retain cycle. - _responseWriteable = [[GRPCDelegateWrapper alloc] initWithWriteable:writeable writer:self]; + _responseWriteable = [[GRXConcurrentWriteable alloc] initWithWriteable:writeable writer:self]; [self sendHeaders:_requestMetadata]; [self invokeCall]; } diff --git a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h b/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h deleted file mode 100644 index 9a30a2f966..0000000000 --- a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#import - -#import - -@protocol GRXWriteable; - -// This is a thread-safe wrapper over a GRXWriteable instance. It lets one -// enqueue calls to a GRXWriteable instance for the main thread, guaranteeing -// that writesFinishedWithError: is the last message sent to it (no matter what -// messages are sent to the wrapper, in what order, nor from which thread). It -// also guarantees that, if cancelWithError: is called from the main thread -// (e.g. by the app cancelling the writes), no further messages are sent to the -// writeable except writesFinishedWithError:. -// -// TODO(jcanizales): Let the user specify another queue for the writeable -// callbacks. -// TODO(jcanizales): Rename to GRXWriteableWrapper and move to the Rx library. -@interface GRPCDelegateWrapper : NSObject - -// The GRXWriteable passed is the wrapped writeable. -// Both the GRXWriter instance and the GRXWriteable instance are retained until -// writesFinishedWithError: is sent to the writeable, and released after that. -// This is used to create a retain cycle that keeps both objects alive until the -// writing is explicitly finished. -- (instancetype)initWithWriteable:(id)writeable writer:(GRXWriter *)writer - NS_DESIGNATED_INITIALIZER; - -// Enqueues writeValue: to be sent to the writeable in the main thread. -// The passed handler is invoked from the main thread after writeValue: returns. -- (void)enqueueMessage:(NSData *)message completionHandler:(void (^)())handler; - -// Enqueues writesFinishedWithError:nil to be sent to the writeable in the main -// thread. After that message is sent to the writeable, all other methods of -// this object are effectively noops. -- (void)enqueueSuccessfulCompletion; - -// If the writeable has not yet received a writesFinishedWithError: message, this -// will enqueue one to be sent to it in the main thread, and cancel all other -// pending messages to the writeable enqueued by this object (both past and -// future). -// The error argument cannot be nil. -- (void)cancelWithError:(NSError *)error; - -// Cancels all pending messages to the writeable enqueued by this object (both -// past and future). Because the writeable won't receive writesFinishedWithError:, -// this also releases the writeable and the writer. -- (void)cancelSilently; -@end diff --git a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m b/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m deleted file mode 100644 index 294cfb7e23..0000000000 --- a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m +++ /dev/null @@ -1,120 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#import "GRPCDelegateWrapper.h" - -#import - -@interface GRPCDelegateWrapper () -// These are atomic so that cancellation can nillify them from any thread. -@property(atomic, strong) id writeable; -@property(atomic, strong) GRXWriter *writer; -@end - -@implementation GRPCDelegateWrapper { - dispatch_queue_t _writeableQueue; - // This ensures that writesFinishedWithError: is only sent once to the writeable. - dispatch_once_t _alreadyFinished; -} - -- (instancetype)init { - return [self initWithWriteable:nil writer:nil]; -} - -// Designated initializer -- (instancetype)initWithWriteable:(id)writeable writer:(GRXWriter *)writer { - if (self = [super init]) { - _writeableQueue = dispatch_get_main_queue(); - _writeable = writeable; - _writer = writer; - } - return self; -} - -- (void)enqueueMessage:(NSData *)message completionHandler:(void (^)())handler { - dispatch_async(_writeableQueue, ^{ - // We're racing a possible cancellation performed by another thread. To turn - // all already-enqueued messages into noops, cancellation nillifies the - // writeable property. If we get it before it's nil, we won - // the race. - id writeable = self.writeable; - if (writeable) { - [writeable writeValue:message]; - handler(); - } - }); -} - -- (void)enqueueSuccessfulCompletion { - dispatch_async(_writeableQueue, ^{ - dispatch_once(&_alreadyFinished, ^{ - // Cancellation is now impossible. None of the other three blocks can run - // concurrently with this one. - [self.writeable writesFinishedWithError:nil]; - // Break the retain cycle with writer, and skip any possible message to the - // wrapped writeable enqueued after this one. - self.writeable = nil; - self.writer = nil; - }); - }); -} - -- (void)cancelWithError:(NSError *)error { - NSAssert(error, @"For a successful completion, use enqueueSuccessfulCompletion."); - dispatch_once(&_alreadyFinished, ^{ - // Skip any of the still-enqueued messages to the wrapped writeable. We use - // the atomic setter to nillify writer and writeable because we might be - // running concurrently with the blocks in _writeableQueue, and assignment - // with ARC isn't atomic. - id writeable = self.writeable; - self.writeable = nil; - - dispatch_async(_writeableQueue, ^{ - [writeable writesFinishedWithError:error]; - // Break the retain cycle with writer. - self.writer = nil; - }); - }); -} - -- (void)cancelSilently { - dispatch_once(&_alreadyFinished, ^{ - // Skip any of the still-enqueued messages to the wrapped writeable. We use - // the atomic setter to nillify writer and writeable because we might be - // running concurrently with the blocks in _writeableQueue, and assignment - // with ARC isn't atomic. - self.writeable = nil; - self.writer = nil; - }); -} -@end diff --git a/src/objective-c/RxLibrary/GRXConcurrentWriteable.h b/src/objective-c/RxLibrary/GRXConcurrentWriteable.h new file mode 100644 index 0000000000..5d1e1dafc0 --- /dev/null +++ b/src/objective-c/RxLibrary/GRXConcurrentWriteable.h @@ -0,0 +1,81 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#import + +#import + +@protocol GRXWriteable; + +// This is a thread-safe wrapper over a GRXWriteable instance. It lets one +// enqueue calls to a GRXWriteable instance for the main thread, guaranteeing +// that writesFinishedWithError: is the last message sent to it (no matter what +// messages are sent to the wrapper, in what order, nor from which thread). It +// also guarantees that, if cancelWithError: is called from the main thread +// (e.g. by the app cancelling the writes), no further messages are sent to the +// writeable except writesFinishedWithError:. +// +// TODO(jcanizales): Let the user specify another queue for the writeable +// callbacks. +// TODO(jcanizales): Rename to GRXWriteableWrapper and move to the Rx library. +@interface GRXConcurrentWriteable : NSObject + +// The GRXWriteable passed is the wrapped writeable. +// Both the GRXWriter instance and the GRXWriteable instance are retained until +// writesFinishedWithError: is sent to the writeable, and released after that. +// This is used to create a retain cycle that keeps both objects alive until the +// writing is explicitly finished. +- (instancetype)initWithWriteable:(id)writeable writer:(GRXWriter *)writer + NS_DESIGNATED_INITIALIZER; + +// Enqueues writeValue: to be sent to the writeable in the main thread. +// The passed handler is invoked from the main thread after writeValue: returns. +- (void)enqueueMessage:(NSData *)message completionHandler:(void (^)())handler; + +// Enqueues writesFinishedWithError:nil to be sent to the writeable in the main +// thread. After that message is sent to the writeable, all other methods of +// this object are effectively noops. +- (void)enqueueSuccessfulCompletion; + +// If the writeable has not yet received a writesFinishedWithError: message, this +// will enqueue one to be sent to it in the main thread, and cancel all other +// pending messages to the writeable enqueued by this object (both past and +// future). +// The error argument cannot be nil. +- (void)cancelWithError:(NSError *)error; + +// Cancels all pending messages to the writeable enqueued by this object (both +// past and future). Because the writeable won't receive writesFinishedWithError:, +// this also releases the writeable and the writer. +- (void)cancelSilently; +@end diff --git a/src/objective-c/RxLibrary/GRXConcurrentWriteable.m b/src/objective-c/RxLibrary/GRXConcurrentWriteable.m new file mode 100644 index 0000000000..b71098d73b --- /dev/null +++ b/src/objective-c/RxLibrary/GRXConcurrentWriteable.m @@ -0,0 +1,120 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#import "GRXConcurrentWriteable.h" + +#import + +@interface GRXConcurrentWriteable () +// These are atomic so that cancellation can nillify them from any thread. +@property(atomic, strong) id writeable; +@property(atomic, strong) GRXWriter *writer; +@end + +@implementation GRXConcurrentWriteable { + dispatch_queue_t _writeableQueue; + // This ensures that writesFinishedWithError: is only sent once to the writeable. + dispatch_once_t _alreadyFinished; +} + +- (instancetype)init { + return [self initWithWriteable:nil writer:nil]; +} + +// Designated initializer +- (instancetype)initWithWriteable:(id)writeable writer:(GRXWriter *)writer { + if (self = [super init]) { + _writeableQueue = dispatch_get_main_queue(); + _writeable = writeable; + _writer = writer; + } + return self; +} + +- (void)enqueueMessage:(NSData *)message completionHandler:(void (^)())handler { + dispatch_async(_writeableQueue, ^{ + // We're racing a possible cancellation performed by another thread. To turn + // all already-enqueued messages into noops, cancellation nillifies the + // writeable property. If we get it before it's nil, we won + // the race. + id writeable = self.writeable; + if (writeable) { + [writeable writeValue:message]; + handler(); + } + }); +} + +- (void)enqueueSuccessfulCompletion { + dispatch_async(_writeableQueue, ^{ + dispatch_once(&_alreadyFinished, ^{ + // Cancellation is now impossible. None of the other three blocks can run + // concurrently with this one. + [self.writeable writesFinishedWithError:nil]; + // Break the retain cycle with writer, and skip any possible message to the + // wrapped writeable enqueued after this one. + self.writeable = nil; + self.writer = nil; + }); + }); +} + +- (void)cancelWithError:(NSError *)error { + NSAssert(error, @"For a successful completion, use enqueueSuccessfulCompletion."); + dispatch_once(&_alreadyFinished, ^{ + // Skip any of the still-enqueued messages to the wrapped writeable. We use + // the atomic setter to nillify writer and writeable because we might be + // running concurrently with the blocks in _writeableQueue, and assignment + // with ARC isn't atomic. + id writeable = self.writeable; + self.writeable = nil; + + dispatch_async(_writeableQueue, ^{ + [writeable writesFinishedWithError:error]; + // Break the retain cycle with writer. + self.writer = nil; + }); + }); +} + +- (void)cancelSilently { + dispatch_once(&_alreadyFinished, ^{ + // Skip any of the still-enqueued messages to the wrapped writeable. We use + // the atomic setter to nillify writer and writeable because we might be + // running concurrently with the blocks in _writeableQueue, and assignment + // with ARC isn't atomic. + self.writeable = nil; + self.writer = nil; + }); +} +@end -- cgit v1.2.3 From 4c6f778cfd4ce9e28f12db10a922c26785e96b94 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Fri, 17 Jul 2015 23:13:36 -0700 Subject: ConcurrentWriteable: NSData *message -> id value --- src/objective-c/GRPCClient/GRPCCall.m | 2 +- src/objective-c/RxLibrary/GRXConcurrentWriteable.h | 8 +++----- src/objective-c/RxLibrary/GRXConcurrentWriteable.m | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) (limited to 'src/objective-c') diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index c41ea41213..0bfc864de3 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -216,7 +216,7 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey"; [weakSelf cancelCall]; return; } - [weakWriteable enqueueMessage:data completionHandler:^{ + [weakWriteable enqueueValue:data completionHandler:^{ [weakSelf startNextRead]; }]; }]; diff --git a/src/objective-c/RxLibrary/GRXConcurrentWriteable.h b/src/objective-c/RxLibrary/GRXConcurrentWriteable.h index 5d1e1dafc0..018af46150 100644 --- a/src/objective-c/RxLibrary/GRXConcurrentWriteable.h +++ b/src/objective-c/RxLibrary/GRXConcurrentWriteable.h @@ -33,9 +33,8 @@ #import -#import - -@protocol GRXWriteable; +#import "GRXWriter.h" +#import "GRXWriteable.h" // This is a thread-safe wrapper over a GRXWriteable instance. It lets one // enqueue calls to a GRXWriteable instance for the main thread, guaranteeing @@ -47,7 +46,6 @@ // // TODO(jcanizales): Let the user specify another queue for the writeable // callbacks. -// TODO(jcanizales): Rename to GRXWriteableWrapper and move to the Rx library. @interface GRXConcurrentWriteable : NSObject // The GRXWriteable passed is the wrapped writeable. @@ -60,7 +58,7 @@ // Enqueues writeValue: to be sent to the writeable in the main thread. // The passed handler is invoked from the main thread after writeValue: returns. -- (void)enqueueMessage:(NSData *)message completionHandler:(void (^)())handler; +- (void)enqueueValue:(id)value completionHandler:(void (^)())handler; // Enqueues writesFinishedWithError:nil to be sent to the writeable in the main // thread. After that message is sent to the writeable, all other methods of diff --git a/src/objective-c/RxLibrary/GRXConcurrentWriteable.m b/src/objective-c/RxLibrary/GRXConcurrentWriteable.m index b71098d73b..735df3137b 100644 --- a/src/objective-c/RxLibrary/GRXConcurrentWriteable.m +++ b/src/objective-c/RxLibrary/GRXConcurrentWriteable.m @@ -61,7 +61,7 @@ return self; } -- (void)enqueueMessage:(NSData *)message completionHandler:(void (^)())handler { +- (void)enqueueValue:(id)value completionHandler:(void (^)())handler { dispatch_async(_writeableQueue, ^{ // We're racing a possible cancellation performed by another thread. To turn // all already-enqueued messages into noops, cancellation nillifies the @@ -69,7 +69,7 @@ // the race. id writeable = self.writeable; if (writeable) { - [writeable writeValue:message]; + [writeable writeValue:value]; handler(); } }); -- cgit v1.2.3 From 6531b2b7915041771b4c1e1496765dd52aae0d26 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Sat, 18 Jul 2015 00:19:14 -0700 Subject: Make the call retain cycle explicit and encapsulated within GRPCCall.m --- src/objective-c/GRPCClient/GRPCCall.m | 24 +++++++++---- src/objective-c/RxLibrary/GRXConcurrentWriteable.h | 42 +++++++++------------- src/objective-c/RxLibrary/GRXConcurrentWriteable.m | 40 ++++++++------------- 3 files changed, 50 insertions(+), 56 deletions(-) (limited to 'src/objective-c') diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index 0bfc864de3..9435bf2b35 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -81,6 +81,10 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey"; GRXConcurrentWriteable *_responseWriteable; GRXWriter *_requestWriter; + // To create a retain cycle when a call is started, up until it finishes. See + // |startWithWriteable:| and |finishWithError:|. + GRPCCall *_self; + NSMutableDictionary *_requestMetadata; NSMutableDictionary *_responseMetadata; } @@ -143,8 +147,13 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey"; #pragma mark Finish - (void)finishWithError:(NSError *)errorOrNil { + // If the call isn't retained anywhere else, it can be deallocated now. + _self = nil; + + // If there were still request messages coming, stop them. _requestWriter.state = GRXWriterStateFinished; _requestWriter = nil; + if (errorOrNil) { [_responseWriteable cancelWithError:errorOrNil]; } else { @@ -276,6 +285,7 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey"; } - (void)writesFinishedWithError:(NSError *)errorOrNil { + _requestWriter = nil; if (errorOrNil) { [self cancel]; } else { @@ -335,12 +345,14 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey"; #pragma mark GRXWriter implementation - (void)startWithWriteable:(id)writeable { - // The following produces a retain cycle self:_responseWriteable:self, which is only - // broken when writesFinishedWithError: is sent to the wrapped writeable. - // Care is taken not to retain self strongly in any of the blocks used in - // the implementation of GRPCCall, so that the life of the instance is - // determined by this retain cycle. - _responseWriteable = [[GRXConcurrentWriteable alloc] initWithWriteable:writeable writer:self]; + // 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. + _self = self; + + _responseWriteable = [[GRXConcurrentWriteable alloc] initWithWriteable:writeable]; [self sendHeaders:_requestMetadata]; [self invokeCall]; } diff --git a/src/objective-c/RxLibrary/GRXConcurrentWriteable.h b/src/objective-c/RxLibrary/GRXConcurrentWriteable.h index 018af46150..1080001905 100644 --- a/src/objective-c/RxLibrary/GRXConcurrentWriteable.h +++ b/src/objective-c/RxLibrary/GRXConcurrentWriteable.h @@ -36,44 +36,36 @@ #import "GRXWriter.h" #import "GRXWriteable.h" -// This is a thread-safe wrapper over a GRXWriteable instance. It lets one -// enqueue calls to a GRXWriteable instance for the main thread, guaranteeing -// that writesFinishedWithError: is the last message sent to it (no matter what -// messages are sent to the wrapper, in what order, nor from which thread). It -// also guarantees that, if cancelWithError: is called from the main thread -// (e.g. by the app cancelling the writes), no further messages are sent to the -// writeable except writesFinishedWithError:. +// This is a thread-safe wrapper over a GRXWriteable instance. It lets one enqueue calls to a +// GRXWriteable instance for the main thread, guaranteeing that writesFinishedWithError: is the last +// message sent to it (no matter what messages are sent to the wrapper, in what order, nor from +// which thread). It also guarantees that, if cancelWithError: is called from the main thread (e.g. +// by the app cancelling the writes), no further messages are sent to the writeable except +// writesFinishedWithError:. // -// TODO(jcanizales): Let the user specify another queue for the writeable -// callbacks. +// TODO(jcanizales): Let the user specify another queue for the writeable callbacks. @interface GRXConcurrentWriteable : NSObject // The GRXWriteable passed is the wrapped writeable. -// Both the GRXWriter instance and the GRXWriteable instance are retained until -// writesFinishedWithError: is sent to the writeable, and released after that. -// This is used to create a retain cycle that keeps both objects alive until the -// writing is explicitly finished. -- (instancetype)initWithWriteable:(id)writeable writer:(GRXWriter *)writer - NS_DESIGNATED_INITIALIZER; +// The GRXWriteable instance is retained until writesFinishedWithError: is sent to it, and released +// after that. +- (instancetype)initWithWriteable:(id)writeable NS_DESIGNATED_INITIALIZER; // Enqueues writeValue: to be sent to the writeable in the main thread. // The passed handler is invoked from the main thread after writeValue: returns. - (void)enqueueValue:(id)value completionHandler:(void (^)())handler; -// Enqueues writesFinishedWithError:nil to be sent to the writeable in the main -// thread. After that message is sent to the writeable, all other methods of -// this object are effectively noops. +// Enqueues writesFinishedWithError:nil to be sent to the writeable in the main thread. After that +// message is sent to the writeable, all other methods of this object are effectively noops. - (void)enqueueSuccessfulCompletion; -// If the writeable has not yet received a writesFinishedWithError: message, this -// will enqueue one to be sent to it in the main thread, and cancel all other -// pending messages to the writeable enqueued by this object (both past and -// future). +// If the writeable has not yet received a writesFinishedWithError: message, this will enqueue one +// to be sent to it in the main thread, and cancel all other pending messages to the writeable +// enqueued by this object (both past and future). // The error argument cannot be nil. - (void)cancelWithError:(NSError *)error; -// Cancels all pending messages to the writeable enqueued by this object (both -// past and future). Because the writeable won't receive writesFinishedWithError:, -// this also releases the writeable and the writer. +// Cancels all pending messages to the writeable enqueued by this object (both past and future). +// Because the writeable won't receive writesFinishedWithError:, this also releases the writeable. - (void)cancelSilently; @end diff --git a/src/objective-c/RxLibrary/GRXConcurrentWriteable.m b/src/objective-c/RxLibrary/GRXConcurrentWriteable.m index 735df3137b..08bd079aea 100644 --- a/src/objective-c/RxLibrary/GRXConcurrentWriteable.m +++ b/src/objective-c/RxLibrary/GRXConcurrentWriteable.m @@ -36,9 +36,8 @@ #import @interface GRXConcurrentWriteable () -// These are atomic so that cancellation can nillify them from any thread. +// This is atomic so that cancellation can nillify it from any thread. @property(atomic, strong) id writeable; -@property(atomic, strong) GRXWriter *writer; @end @implementation GRXConcurrentWriteable { @@ -48,25 +47,23 @@ } - (instancetype)init { - return [self initWithWriteable:nil writer:nil]; + return [self initWithWriteable:nil]; } // Designated initializer -- (instancetype)initWithWriteable:(id)writeable writer:(GRXWriter *)writer { +- (instancetype)initWithWriteable:(id)writeable { if (self = [super init]) { _writeableQueue = dispatch_get_main_queue(); _writeable = writeable; - _writer = writer; } return self; } - (void)enqueueValue:(id)value completionHandler:(void (^)())handler { dispatch_async(_writeableQueue, ^{ - // We're racing a possible cancellation performed by another thread. To turn - // all already-enqueued messages into noops, cancellation nillifies the - // writeable property. If we get it before it's nil, we won - // the race. + // We're racing a possible cancellation performed by another thread. To turn all already- + // enqueued messages into noops, cancellation nillifies the writeable property. If we get it + // before it's nil, we won the race. id writeable = self.writeable; if (writeable) { [writeable writeValue:value]; @@ -78,13 +75,11 @@ - (void)enqueueSuccessfulCompletion { dispatch_async(_writeableQueue, ^{ dispatch_once(&_alreadyFinished, ^{ - // Cancellation is now impossible. None of the other three blocks can run - // concurrently with this one. + // Cancellation is now impossible. None of the other three blocks can run concurrently with + // this one. [self.writeable writesFinishedWithError:nil]; - // Break the retain cycle with writer, and skip any possible message to the - // wrapped writeable enqueued after this one. + // Skip any possible message to the wrapped writeable enqueued after this one. self.writeable = nil; - self.writer = nil; }); }); } @@ -92,29 +87,24 @@ - (void)cancelWithError:(NSError *)error { NSAssert(error, @"For a successful completion, use enqueueSuccessfulCompletion."); dispatch_once(&_alreadyFinished, ^{ - // Skip any of the still-enqueued messages to the wrapped writeable. We use - // the atomic setter to nillify writer and writeable because we might be - // running concurrently with the blocks in _writeableQueue, and assignment - // with ARC isn't atomic. + // Skip any of the still-enqueued messages to the wrapped writeable. We use the atomic setter to + // nillify writeable because we might be running concurrently with the blocks in + // _writeableQueue, and assignment with ARC isn't atomic. id writeable = self.writeable; self.writeable = nil; dispatch_async(_writeableQueue, ^{ [writeable writesFinishedWithError:error]; - // Break the retain cycle with writer. - self.writer = nil; }); }); } - (void)cancelSilently { dispatch_once(&_alreadyFinished, ^{ - // Skip any of the still-enqueued messages to the wrapped writeable. We use - // the atomic setter to nillify writer and writeable because we might be - // running concurrently with the blocks in _writeableQueue, and assignment - // with ARC isn't atomic. + // Skip any of the still-enqueued messages to the wrapped writeable. We use the atomic setter to + // nillify writeable because we might be running concurrently with the blocks in + // _writeableQueue, and assignment with ARC isn't atomic. self.writeable = nil; - self.writer = nil; }); } @end -- cgit v1.2.3 From 243e8ee4ebc85138f9d8d6426e9aa56e455fe796 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Fri, 17 Jul 2015 19:13:58 -0700 Subject: Remove Cocoapods warning of OTHER_LDFLAGS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don’t use the Tests library target anyway. --- src/objective-c/tests/Tests.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/objective-c') diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj index 34be705db2..f13fb8288b 100644 --- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj @@ -391,7 +391,6 @@ 635697DC1B14FC11007A7283 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; @@ -400,7 +399,6 @@ 635697DD1B14FC11007A7283 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; -- cgit v1.2.3 From 570e9417b62fd85e1aa50e873c93ebe0ff2a0d5a Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Fri, 17 Jul 2015 21:17:39 -0700 Subject: Fix C library gpr_inf_future breakage --- src/objective-c/GRPCClient/private/GRPCCompletionQueue.m | 3 ++- src/objective-c/GRPCClient/private/GRPCWrappedCall.m | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src/objective-c') diff --git a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m index 40aade4f9a..12535c9616 100644 --- a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m +++ b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m @@ -65,7 +65,8 @@ dispatch_async(gDefaultConcurrentQueue, ^{ while (YES) { // The following call blocks until an event is available. - grpc_event event = grpc_completion_queue_next(unmanagedQueue, gpr_inf_future); + grpc_event event = grpc_completion_queue_next(unmanagedQueue, + gpr_inf_future(GPR_CLOCK_REALTIME)); GRPCQueueCompletionHandler handler; switch (event.type) { case GRPC_OP_COMPLETE: diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m index 45f10f5d63..1db63df77f 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m @@ -246,8 +246,11 @@ if (!_queue) { return nil; } - _call = grpc_channel_create_call(channel.unmanagedChannel, _queue.unmanagedQueue, - path.UTF8String, host.UTF8String, gpr_inf_future); + _call = grpc_channel_create_call(channel.unmanagedChannel, + _queue.unmanagedQueue, + path.UTF8String, + host.UTF8String, + gpr_inf_future(GPR_CLOCK_REALTIME)); if (_call == NULL) { return nil; } -- cgit v1.2.3 From bc970ae23b90c443ed8ebb379f4bebda2ceef833 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Sun, 19 Jul 2015 12:27:20 -0700 Subject: Fix [GRXWriter emptyWriter] not being reusable. --- src/objective-c/RxLibrary/GRXImmediateWriter.m | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) (limited to 'src/objective-c') diff --git a/src/objective-c/RxLibrary/GRXImmediateWriter.m b/src/objective-c/RxLibrary/GRXImmediateWriter.m index b6d2b2cac0..3edae788ab 100644 --- a/src/objective-c/RxLibrary/GRXImmediateWriter.m +++ b/src/objective-c/RxLibrary/GRXImmediateWriter.m @@ -76,28 +76,15 @@ } + (GRXWriter *)writerWithValue:(id)value { - if (value) { - return [self writerWithEnumerator:[NSEnumerator grx_enumeratorWithSingleValue:value]]; - } else { - return [self emptyWriter]; - } + return [self writerWithEnumerator:[NSEnumerator grx_enumeratorWithSingleValue:value]]; } + (GRXWriter *)writerWithError:(NSError *)error { - if (error) { - return [self writerWithEnumerator:nil error:error]; - } else { - return [self emptyWriter]; - } + return [self writerWithEnumerator:nil error:error]; } + (GRXWriter *)emptyWriter { - static GRXImmediateWriter *emptyWriter; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - emptyWriter = [self writerWithEnumerator:nil error:nil]; - }); - return emptyWriter; + return [self writerWithEnumerator:nil error:nil]; } #pragma mark Conformance with GRXWriter -- cgit v1.2.3 From 5fcdffcf3e5efb629a686da38fb61e0a5dcf05db Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Sun, 19 Jul 2015 12:27:58 -0700 Subject: Accept [GRXWriter writerWithContainer:nil] for consistency with the rest --- src/objective-c/RxLibrary/private/GRXNSFastEnumerator.m | 1 - 1 file changed, 1 deletion(-) (limited to 'src/objective-c') diff --git a/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.m b/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.m index 2050fa98ec..0387c9954e 100644 --- a/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.m +++ b/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.m @@ -59,7 +59,6 @@ // Designated initializer. - (instancetype)initWithContainer:(id)container { - NSAssert(container, @"container can't be nil"); if ((self = [super init])) { _container = container; } -- cgit v1.2.3