From 142acc965e12b950c4b3d52df22ffaa28b6b62e5 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Fri, 15 May 2015 18:43:34 -0700 Subject: Adds GRXBufferedPipe --- src/objective-c/RxLibrary/GRXBufferedPipe.h | 53 +++++++++++ src/objective-c/RxLibrary/GRXBufferedPipe.m | 142 ++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 src/objective-c/RxLibrary/GRXBufferedPipe.h create mode 100644 src/objective-c/RxLibrary/GRXBufferedPipe.m (limited to 'src/objective-c') diff --git a/src/objective-c/RxLibrary/GRXBufferedPipe.h b/src/objective-c/RxLibrary/GRXBufferedPipe.h new file mode 100644 index 0000000000..02dc6b5fe8 --- /dev/null +++ b/src/objective-c/RxLibrary/GRXBufferedPipe.h @@ -0,0 +1,53 @@ +/* + * + * 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 "GRXWriteable.h" +#import "GRXWriter.h" + +// A buffered pipe is a Writeable that also acts as a Writer (to whichever other writeable is passed +// to -startWithWriteable:). +// Once it is started, whatever values are written into it (via -didReceiveValue:) will be +// propagated immediately, unless flow control prevents it. +// If it is throttled and keeps receiving values, as well as if it receives values before being +// started, it will buffer them and propagate them in order as soon as its state becomes +// GRXWriterStateStarted. +// If it receives an error (via -didFinishWithError:), it will drop any buffered values and +// propagate the error immediately. +@interface GRXBufferedPipe : NSObject + +// Convenience constructor. ++ (instancetype)pipe; + +@end diff --git a/src/objective-c/RxLibrary/GRXBufferedPipe.m b/src/objective-c/RxLibrary/GRXBufferedPipe.m new file mode 100644 index 0000000000..e1295cef8a --- /dev/null +++ b/src/objective-c/RxLibrary/GRXBufferedPipe.m @@ -0,0 +1,142 @@ +/* + * + * 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 "GRXBufferedPipe.h" + +@implementation GRXBufferedPipe { + id _writeable; + NSMutableArray *_queue; + BOOL _inputIsFinished; + NSError *_errorOrNil; +} + +@synthesize state = _state; + ++ (instancetype)pipe { + return [[self alloc] init]; +} + +- (instancetype)init { + if (self = [super init]) { + _queue = [NSMutableArray array]; + _state = GRXWriterStateNotStarted; + } + return self; +} + +- (id)popValue { + id value = _queue[0]; + [_queue removeObjectAtIndex:0]; + return value; +} + +- (void)writeBufferUntilPausedOrStopped { + while (_state == GRXWriterStateStarted && _queue.count > 0) { + [_writeable didReceiveValue:[self popValue]]; + } + if (_inputIsFinished && _queue.count == 0) { + // Our writer finished normally while we were paused or not-started-yet. + [self finishWithError:_errorOrNil]; + } +} + +#pragma mark GRXWriteable implementation + +// Returns whether events can be simply propagated to the other end of the pipe. +- (BOOL)shouldFastForward { + return _state == GRXWriterStateStarted && _queue.count == 0; +} + +- (void)didReceiveValue:(id)value { + if (self.shouldFastForward) { + // Skip the queue. + [_writeable didReceiveValue:value]; + } else { + // Even if we're paused and with enqueued values, we can't excert back-pressure to our writer. + // So just buffer the new value. + [_queue addObject:value]; + } +} + +- (void)didFinishWithError:(NSError *)errorOrNil { + _inputIsFinished = YES; + _errorOrNil = errorOrNil; + if (errorOrNil || self.shouldFastForward) { + // No need to write pending values. + [self finishWithError:_errorOrNil]; + } +} + +#pragma mark GRXWriter implementation + +- (void)setState:(GRXWriterState)newState { + // Manual transitions are only allowed from the started or paused states. + if (_state == GRXWriterStateNotStarted || _state == GRXWriterStateFinished) { + return; + } + + switch (newState) { + case GRXWriterStateFinished: + _state = newState; + _queue = nil; + // Per GRXWriter's contract, setting the state to Finished manually means one doesn't wish the + // writeable to be messaged anymore. + _writeable = nil; + return; + case GRXWriterStatePaused: + _state = newState; + return; + case GRXWriterStateStarted: + if (_state == GRXWriterStatePaused) { + _state = newState; + [self writeBufferUntilPausedOrStopped]; + } + return; + case GRXWriterStateNotStarted: + return; + } +} + +- (void)startWithWriteable:(id)writeable { + _state = GRXWriterStateStarted; + _writeable = writeable; + [self writeBufferUntilPausedOrStopped]; +} + +- (void)finishWithError:(NSError *)errorOrNil { + id writeable = _writeable; + self.state = GRXWriterStateFinished; + [writeable didFinishWithError:errorOrNil]; +} + +@end -- cgit v1.2.3 From 421f6c9488795a1a125770f258b9cf4feef4f293 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Mon, 18 May 2015 11:51:12 -0700 Subject: Use BufferedPipe to add PingPong test and fix Cancel test --- .../examples/Sample/SampleTests/RemoteProtoTests.m | 98 +++++++++++++++++++--- 1 file changed, 87 insertions(+), 11 deletions(-) (limited to 'src/objective-c') diff --git a/src/objective-c/examples/Sample/SampleTests/RemoteProtoTests.m b/src/objective-c/examples/Sample/SampleTests/RemoteProtoTests.m index f489229834..7cebc0c2a7 100644 --- a/src/objective-c/examples/Sample/SampleTests/RemoteProtoTests.m +++ b/src/objective-c/examples/Sample/SampleTests/RemoteProtoTests.m @@ -36,13 +36,46 @@ #import #import -#import #import +#import +#import #import #import #import #import +// Convenience constructors for the generated proto messages: + +@interface RMTStreamingOutputCallRequest (Constructors) ++ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize + requestedResponseSize:(NSNumber *)responseSize; +@end + +@implementation RMTStreamingOutputCallRequest (Constructors) ++ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize + requestedResponseSize:(NSNumber *)responseSize { + RMTStreamingOutputCallRequest *request = [self message]; + RMTResponseParameters *parameters = [RMTResponseParameters message]; + parameters.size = responseSize.integerValue; + [request.responseParametersArray addObject:parameters]; + request.payload.body = [NSMutableData dataWithLength:payloadSize.unsignedIntegerValue]; + return request; +} +@end + +@interface RMTStreamingOutputCallResponse (Constructors) ++ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize; +@end + +@implementation RMTStreamingOutputCallResponse (Constructors) ++ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize { + RMTStreamingOutputCallResponse * response = [self message]; + response.payload.type = RMTPayloadType_Compressable; + response.payload.body = [NSMutableData dataWithLength:payloadSize.unsignedIntegerValue]; + return response; +} +@end + @interface RemoteProtoTests : XCTestCase @end @@ -70,7 +103,7 @@ [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:2. handler:nil]; + [self waitForExpectationsWithTimeout:2 handler:nil]; } - (void)testLargeUnaryRPC { @@ -92,7 +125,7 @@ [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:4. handler:nil]; + [self waitForExpectationsWithTimeout:4 handler:nil]; } - (void)testClientStreamingRPC { @@ -124,7 +157,7 @@ [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:4. handler:nil]; + [self waitForExpectationsWithTimeout:4 handler:nil]; } - (void)testServerStreamingRPC { @@ -149,10 +182,7 @@ if (response) { XCTAssertLessThan(index, 4, @"More than 4 responses received."); - RMTStreamingOutputCallResponse * expected = [RMTStreamingOutputCallResponse message]; - expected.payload.type = RMTPayloadType_Compressable; - int expectedSize = [expectedSizes[index] unsignedIntegerValue]; - expected.payload.body = [NSMutableData dataWithLength:expectedSize]; + id expected = [RMTStreamingOutputCallResponse messageWithPayloadSize:expectedSizes[index]]; XCTAssertEqualObjects(response, expected); index += 1; } @@ -166,6 +196,49 @@ [self waitForExpectationsWithTimeout:4 handler:nil]; } +- (void)testPingPongRPC { + __weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPong"]; + + NSArray *requests = @[@27182, @8, @1828, @45904]; + NSArray *responses = @[@31415, @9, @2653, @58979]; + + GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; + + __block int index = 0; + + id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] + requestedResponseSize:responses[index]]; + [requestsBuffer didReceiveValue:request]; + + [_service fullDuplexCallWithRequestsWriter:requestsBuffer + handler:^(BOOL done, + RMTStreamingOutputCallResponse *response, + NSError *error) { + XCTAssertNil(error, @"Finished with unexpected error: %@", error); + XCTAssertTrue(done || response, @"Event handler called without an event."); + + if (response) { + XCTAssertLessThan(index, 4, @"More than 4 responses received."); + id expected = [RMTStreamingOutputCallResponse messageWithPayloadSize:responses[index]]; + XCTAssertEqualObjects(response, expected); + index += 1; + if (index < 4) { + id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] + requestedResponseSize:responses[index]]; + [requestsBuffer didReceiveValue:request]; + } else { + [requestsBuffer didFinishWithError:nil]; + } + } + + if (done) { + XCTAssertEqual(index, 4, @"Received %i responses instead of 4.", index); + [expectation fulfill]; + } + }]; + [self waitForExpectationsWithTimeout:2 handler:nil]; +} + - (void)testEmptyStreamRPC { __weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyStream"]; [_service fullDuplexCallWithRequestsWriter:[GRXWriter emptyWriter] @@ -176,13 +249,16 @@ XCTAssert(done, @"Unexpected response: %@", response); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:4 handler:nil]; + [self waitForExpectationsWithTimeout:2 handler:nil]; } - (void)testCancelAfterBeginRPC { __weak XCTestExpectation *expectation = [self expectationWithDescription:@"CancelAfterBegin"]; - // TODO(mlumish): change to writing that blocks instead of writing - ProtoRPC *call = [_service RPCToStreamingInputCallWithRequestsWriter:[GRXWriter emptyWriter] + + // A buffered pipe to which we never write any value acts as a writer that just hangs. + GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; + + ProtoRPC *call = [_service RPCToStreamingInputCallWithRequestsWriter:requestsBuffer handler:^(RMTStreamingInputCallResponse *response, NSError *error) { XCTAssertEqual(error.code, GRPC_STATUS_CANCELLED); -- cgit v1.2.3 From 63e4091dbc0902ea0447bb30940f757478abdef7 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Mon, 18 May 2015 17:41:20 -0700 Subject: Add warning about the perils of buffering without bounds --- src/objective-c/RxLibrary/GRXBufferedPipe.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/objective-c') diff --git a/src/objective-c/RxLibrary/GRXBufferedPipe.h b/src/objective-c/RxLibrary/GRXBufferedPipe.h index 02dc6b5fe8..4147362ba4 100644 --- a/src/objective-c/RxLibrary/GRXBufferedPipe.h +++ b/src/objective-c/RxLibrary/GRXBufferedPipe.h @@ -45,6 +45,12 @@ // GRXWriterStateStarted. // If it receives an error (via -didFinishWithError:), it will drop any buffered values and // propagate the error immediately. +// +// Beware that a pipe of this type can't prevent receiving more values when it is paused (for +// example if used to write data to a congested network connection). Because in such situations the +// pipe will keep buffering all data written to it, your application could run out of memory and +// crash. If you want to react to flow control signals to prevent that, instead of using this class +// you can implement an object that conforms to GRXWriter. @interface GRXBufferedPipe : NSObject // Convenience constructor. -- cgit v1.2.3 From 42e47b462ea4c45496e2367e3bfc168ab433600b Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Mon, 18 May 2015 23:38:17 -0700 Subject: Copy values before buffering them. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So they aren’t mutated before being written later. --- src/objective-c/RxLibrary/GRXBufferedPipe.m | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/objective-c') diff --git a/src/objective-c/RxLibrary/GRXBufferedPipe.m b/src/objective-c/RxLibrary/GRXBufferedPipe.m index e1295cef8a..3ef470f89f 100644 --- a/src/objective-c/RxLibrary/GRXBufferedPipe.m +++ b/src/objective-c/RxLibrary/GRXBufferedPipe.m @@ -84,6 +84,10 @@ } else { // Even if we're paused and with enqueued values, we can't excert back-pressure to our writer. // So just buffer the new value. + // We need a copy, so that it doesn't mutate before it's written at the other end of the pipe. + if ([value respondsToSelector:@selector(copy)]) { + value = [value copy]; + } [_queue addObject:value]; } } -- cgit v1.2.3 From a90a9c395d86b5f1e6b123ae25de14c7f0362040 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Mon, 18 May 2015 17:12:41 -0700 Subject: s/didReceiveValue/writeValue --- src/objective-c/GRPCClient/GRPCCall.m | 2 +- src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h | 5 ++--- src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m | 2 +- src/objective-c/ProtoRPC/ProtoRPC.m | 2 +- src/objective-c/RxLibrary/GRXBufferedPipe.h | 4 ++-- src/objective-c/RxLibrary/GRXBufferedPipe.m | 6 +++--- src/objective-c/RxLibrary/GRXImmediateWriter.m | 2 +- src/objective-c/RxLibrary/GRXWriteable.h | 5 ++--- src/objective-c/RxLibrary/GRXWriteable.m | 2 +- src/objective-c/RxLibrary/GRXWriter.m | 4 ++-- src/objective-c/RxLibrary/transformations/GRXMappingWriter.m | 4 ++-- src/objective-c/examples/Sample/SampleTests/RemoteProtoTests.m | 4 ++-- 12 files changed, 20 insertions(+), 22 deletions(-) (limited to 'src/objective-c') diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index 0f458b40cd..b909c0944b 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -231,7 +231,7 @@ handler:resumingHandler]] errorHandler:errorHandler]; } -- (void)didReceiveValue:(id)value { +- (void)writeValue:(id)value { // TODO(jcanizales): Throw/assert if value isn't NSData. // Pause the input and only resume it when the C layer notifies us that writes diff --git a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h b/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h index 24c2b96729..6103d4829d 100644 --- a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h +++ b/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h @@ -57,9 +57,8 @@ - (instancetype)initWithWriteable:(id)writeable writer:(id)writer NS_DESIGNATED_INITIALIZER; -// Enqueues didReceiveValue: to be sent to the writeable in the main thread. -// The passed handler is invoked from the main thread after didReceiveValue: -// returns. +// 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 didFinishWithError:nil to be sent to the writeable in the main diff --git a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m b/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m index ac444ef406..b8f206ad23 100644 --- a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m +++ b/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m @@ -69,7 +69,7 @@ // the race. id writeable = self.writeable; if (writeable) { - [writeable didReceiveValue:message]; + [writeable writeValue:message]; handler(); } }); diff --git a/src/objective-c/ProtoRPC/ProtoRPC.m b/src/objective-c/ProtoRPC/ProtoRPC.m index c5051c0123..e4bc48a69f 100644 --- a/src/objective-c/ProtoRPC/ProtoRPC.m +++ b/src/objective-c/ProtoRPC/ProtoRPC.m @@ -71,7 +71,7 @@ if ((self = [super initWithHost:host method:method requestsWriter:bytesWriter])) { // A writeable that parses the proto messages received. _responseWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { - [responsesWriteable didReceiveValue:[responseClass parseFromData:value]]; + [responsesWriteable writeValue:[responseClass parseFromData:value]]; } completionHandler:^(NSError *errorOrNil) { [responsesWriteable didFinishWithError:errorOrNil]; }]; diff --git a/src/objective-c/RxLibrary/GRXBufferedPipe.h b/src/objective-c/RxLibrary/GRXBufferedPipe.h index 02dc6b5fe8..86d12853b0 100644 --- a/src/objective-c/RxLibrary/GRXBufferedPipe.h +++ b/src/objective-c/RxLibrary/GRXBufferedPipe.h @@ -38,8 +38,8 @@ // A buffered pipe is a Writeable that also acts as a Writer (to whichever other writeable is passed // to -startWithWriteable:). -// Once it is started, whatever values are written into it (via -didReceiveValue:) will be -// propagated immediately, unless flow control prevents it. +// Once it is started, whatever values are written into it (via -writeValue:) will be propagated +// immediately, unless flow control prevents it. // If it is throttled and keeps receiving values, as well as if it receives values before being // started, it will buffer them and propagate them in order as soon as its state becomes // GRXWriterStateStarted. diff --git a/src/objective-c/RxLibrary/GRXBufferedPipe.m b/src/objective-c/RxLibrary/GRXBufferedPipe.m index e1295cef8a..0230f14c36 100644 --- a/src/objective-c/RxLibrary/GRXBufferedPipe.m +++ b/src/objective-c/RxLibrary/GRXBufferedPipe.m @@ -62,7 +62,7 @@ - (void)writeBufferUntilPausedOrStopped { while (_state == GRXWriterStateStarted && _queue.count > 0) { - [_writeable didReceiveValue:[self popValue]]; + [_writeable writeValue:[self popValue]]; } if (_inputIsFinished && _queue.count == 0) { // Our writer finished normally while we were paused or not-started-yet. @@ -77,10 +77,10 @@ return _state == GRXWriterStateStarted && _queue.count == 0; } -- (void)didReceiveValue:(id)value { +- (void)writeValue:(id)value { if (self.shouldFastForward) { // Skip the queue. - [_writeable didReceiveValue:value]; + [_writeable writeValue:value]; } else { // Even if we're paused and with enqueued values, we can't excert back-pressure to our writer. // So just buffer the new value. diff --git a/src/objective-c/RxLibrary/GRXImmediateWriter.m b/src/objective-c/RxLibrary/GRXImmediateWriter.m index 7468af557f..7108a56584 100644 --- a/src/objective-c/RxLibrary/GRXImmediateWriter.m +++ b/src/objective-c/RxLibrary/GRXImmediateWriter.m @@ -109,7 +109,7 @@ - (void)writeUntilPausedOrStopped { id value; while (value = [_enumerator nextObject]) { - [_writeable didReceiveValue:value]; + [_writeable writeValue:value]; // If the writeable has a reference to us, it might change our state to paused or finished. if (_state == GRXWriterStatePaused || _state == GRXWriterStateFinished) { return; diff --git a/src/objective-c/RxLibrary/GRXWriteable.h b/src/objective-c/RxLibrary/GRXWriteable.h index 6f6ea142e0..d1bcb51799 100644 --- a/src/objective-c/RxLibrary/GRXWriteable.h +++ b/src/objective-c/RxLibrary/GRXWriteable.h @@ -38,11 +38,10 @@ @protocol GRXWriteable // Push the next value of the sequence to the receiving object. -// TODO(jcanizales): Name it enumerator:(id) didProduceValue:(id)? -- (void)didReceiveValue:(id)value; +- (void)writeValue:(id)value; // Signal that the sequence is completed, or that an error ocurred. After this -// message is sent to the instance, neither it nor didReceiveValue: may be +// message is sent to the instance, neither it nor writeValue: may be // called again. // TODO(jcanizales): enumerator:(id) didFinishWithError:(NSError*)? - (void)didFinishWithError:(NSError *)errorOrNil; diff --git a/src/objective-c/RxLibrary/GRXWriteable.m b/src/objective-c/RxLibrary/GRXWriteable.m index 7000a078d1..d3ee7fa170 100644 --- a/src/objective-c/RxLibrary/GRXWriteable.m +++ b/src/objective-c/RxLibrary/GRXWriteable.m @@ -76,7 +76,7 @@ return self; } -- (void)didReceiveValue:(id)value { +- (void)writeValue:(id)value { if (_valueHandler) { _valueHandler(value); } diff --git a/src/objective-c/RxLibrary/GRXWriter.m b/src/objective-c/RxLibrary/GRXWriter.m index b48a44f3a7..140284b938 100644 --- a/src/objective-c/RxLibrary/GRXWriter.m +++ b/src/objective-c/RxLibrary/GRXWriter.m @@ -75,8 +75,8 @@ #pragma mark GRXWriteable implementation -- (void)didReceiveValue:(id)value { - [_writeable didReceiveValue:value]; +- (void)writeValue:(id)value { + [_writeable writeValue:value]; } - (void)didFinishWithError:(NSError *)errorOrNil { diff --git a/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m index 8a41c819a6..2cdfea1b67 100644 --- a/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m +++ b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m @@ -57,7 +57,7 @@ static id (^kIdentity)(id value) = ^id(id value) { } // Override -- (void)didReceiveValue:(id)value { - [super didReceiveValue:_map(value)]; +- (void)writeValue:(id)value { + [super writeValue:_map(value)]; } @end diff --git a/src/objective-c/examples/Sample/SampleTests/RemoteProtoTests.m b/src/objective-c/examples/Sample/SampleTests/RemoteProtoTests.m index 7cebc0c2a7..51965a1dd7 100644 --- a/src/objective-c/examples/Sample/SampleTests/RemoteProtoTests.m +++ b/src/objective-c/examples/Sample/SampleTests/RemoteProtoTests.m @@ -208,7 +208,7 @@ id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] requestedResponseSize:responses[index]]; - [requestsBuffer didReceiveValue:request]; + [requestsBuffer writeValue:request]; [_service fullDuplexCallWithRequestsWriter:requestsBuffer handler:^(BOOL done, @@ -225,7 +225,7 @@ if (index < 4) { id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] requestedResponseSize:responses[index]]; - [requestsBuffer didReceiveValue:request]; + [requestsBuffer writeValue:request]; } else { [requestsBuffer didFinishWithError:nil]; } -- cgit v1.2.3 From b2c300c4be537c54f3adcaf3ca190b9bce002fab Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Mon, 18 May 2015 17:19:16 -0700 Subject: s/didFinishWithError/writesFinishedWithError --- src/objective-c/GRPCClient/GRPCCall.m | 4 ++-- src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h | 12 ++++++------ src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m | 6 +++--- src/objective-c/ProtoRPC/ProtoRPC.m | 2 +- src/objective-c/RxLibrary/GRXBufferedPipe.h | 2 +- src/objective-c/RxLibrary/GRXBufferedPipe.m | 4 ++-- src/objective-c/RxLibrary/GRXImmediateWriter.m | 2 +- src/objective-c/RxLibrary/GRXWriteable.h | 3 +-- src/objective-c/RxLibrary/GRXWriteable.m | 2 +- src/objective-c/RxLibrary/GRXWriter.h | 6 +++--- src/objective-c/RxLibrary/GRXWriter.m | 4 ++-- .../examples/Sample/SampleTests/RemoteProtoTests.m | 2 +- 12 files changed, 24 insertions(+), 25 deletions(-) (limited to 'src/objective-c') diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index b909c0944b..a4a0ddb324 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -255,7 +255,7 @@ errorHandler:errorHandler]; } -- (void)didFinishWithError:(NSError *)errorOrNil { +- (void)writesFinishedWithError:(NSError *)errorOrNil { if (errorOrNil) { [self cancel]; } else { @@ -306,7 +306,7 @@ - (void)startWithWriteable:(id)writeable { // The following produces a retain cycle self:_responseWriteable:self, which is only - // broken when didFinishWithError: is sent to the wrapped writeable. + // 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. diff --git a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h b/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h index 6103d4829d..1ef245fe37 100644 --- a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h +++ b/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h @@ -38,11 +38,11 @@ // 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 didFinishWithError: is the last message sent to it (no matter what +// 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 didFinishWithError:. +// writeable except writesFinishedWithError:. // // TODO(jcanizales): Let the user specify another queue for the writeable // callbacks. @@ -51,7 +51,7 @@ // The GRXWriteable passed is the wrapped writeable. // Both the GRXWriter instance and the GRXWriteable instance are retained until -// didFinishWithError: is sent to the writeable, and released after that. +// 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:(id)writer @@ -61,12 +61,12 @@ // The passed handler is invoked from the main thread after writeValue: returns. - (void)enqueueMessage:(NSData *)message completionHandler:(void (^)())handler; -// Enqueues didFinishWithError:nil to be sent to the writeable in the main +// 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 didFinishWithError: message, this +// 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). @@ -74,7 +74,7 @@ - (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 didFinishWithError:, +// 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 index b8f206ad23..7d5ecb56d9 100644 --- a/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m +++ b/src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m @@ -43,7 +43,7 @@ @implementation GRPCDelegateWrapper { dispatch_queue_t _writeableQueue; - // This ensures that didFinishWithError: is only sent once to the writeable. + // This ensures that writesFinishedWithError: is only sent once to the writeable. dispatch_once_t _alreadyFinished; } @@ -80,7 +80,7 @@ dispatch_once(&_alreadyFinished, ^{ // Cancellation is now impossible. None of the other three blocks can run // concurrently with this one. - [self.writeable didFinishWithError:nil]; + [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; @@ -100,7 +100,7 @@ self.writeable = nil; dispatch_async(_writeableQueue, ^{ - [writeable didFinishWithError:error]; + [writeable writesFinishedWithError:error]; // Break the retain cycle with writer. self.writer = nil; }); diff --git a/src/objective-c/ProtoRPC/ProtoRPC.m b/src/objective-c/ProtoRPC/ProtoRPC.m index e4bc48a69f..96608f2898 100644 --- a/src/objective-c/ProtoRPC/ProtoRPC.m +++ b/src/objective-c/ProtoRPC/ProtoRPC.m @@ -73,7 +73,7 @@ _responseWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { [responsesWriteable writeValue:[responseClass parseFromData:value]]; } completionHandler:^(NSError *errorOrNil) { - [responsesWriteable didFinishWithError:errorOrNil]; + [responsesWriteable writesFinishedWithError:errorOrNil]; }]; } return self; diff --git a/src/objective-c/RxLibrary/GRXBufferedPipe.h b/src/objective-c/RxLibrary/GRXBufferedPipe.h index 86d12853b0..68a1695a4b 100644 --- a/src/objective-c/RxLibrary/GRXBufferedPipe.h +++ b/src/objective-c/RxLibrary/GRXBufferedPipe.h @@ -43,7 +43,7 @@ // If it is throttled and keeps receiving values, as well as if it receives values before being // started, it will buffer them and propagate them in order as soon as its state becomes // GRXWriterStateStarted. -// If it receives an error (via -didFinishWithError:), it will drop any buffered values and +// If it receives an error (via -writesFinishedWithError:), it will drop any buffered values and // propagate the error immediately. @interface GRXBufferedPipe : NSObject diff --git a/src/objective-c/RxLibrary/GRXBufferedPipe.m b/src/objective-c/RxLibrary/GRXBufferedPipe.m index 0230f14c36..e14e15df70 100644 --- a/src/objective-c/RxLibrary/GRXBufferedPipe.m +++ b/src/objective-c/RxLibrary/GRXBufferedPipe.m @@ -88,7 +88,7 @@ } } -- (void)didFinishWithError:(NSError *)errorOrNil { +- (void)writesFinishedWithError:(NSError *)errorOrNil { _inputIsFinished = YES; _errorOrNil = errorOrNil; if (errorOrNil || self.shouldFastForward) { @@ -136,7 +136,7 @@ - (void)finishWithError:(NSError *)errorOrNil { id writeable = _writeable; self.state = GRXWriterStateFinished; - [writeable didFinishWithError:errorOrNil]; + [writeable writesFinishedWithError:errorOrNil]; } @end diff --git a/src/objective-c/RxLibrary/GRXImmediateWriter.m b/src/objective-c/RxLibrary/GRXImmediateWriter.m index 7108a56584..0b4979872e 100644 --- a/src/objective-c/RxLibrary/GRXImmediateWriter.m +++ b/src/objective-c/RxLibrary/GRXImmediateWriter.m @@ -130,7 +130,7 @@ _errorOrNil = nil; id writeable = _writeable; _writeable = nil; - [writeable didFinishWithError:errorOrNil]; + [writeable writesFinishedWithError:errorOrNil]; } - (void)setState:(GRXWriterState)newState { diff --git a/src/objective-c/RxLibrary/GRXWriteable.h b/src/objective-c/RxLibrary/GRXWriteable.h index d1bcb51799..216de30735 100644 --- a/src/objective-c/RxLibrary/GRXWriteable.h +++ b/src/objective-c/RxLibrary/GRXWriteable.h @@ -43,8 +43,7 @@ // Signal that the sequence is completed, or that an error ocurred. After this // message is sent to the instance, neither it nor writeValue: may be // called again. -// TODO(jcanizales): enumerator:(id) didFinishWithError:(NSError*)? -- (void)didFinishWithError:(NSError *)errorOrNil; +- (void)writesFinishedWithError:(NSError *)errorOrNil; @end typedef void (^GRXValueHandler)(id value); diff --git a/src/objective-c/RxLibrary/GRXWriteable.m b/src/objective-c/RxLibrary/GRXWriteable.m index d3ee7fa170..63f7c3e7f3 100644 --- a/src/objective-c/RxLibrary/GRXWriteable.m +++ b/src/objective-c/RxLibrary/GRXWriteable.m @@ -82,7 +82,7 @@ } } -- (void)didFinishWithError:(NSError *)errorOrNil { +- (void)writesFinishedWithError:(NSError *)errorOrNil { if (_completionHandler) { _completionHandler(errorOrNil); } diff --git a/src/objective-c/RxLibrary/GRXWriter.h b/src/objective-c/RxLibrary/GRXWriter.h index 68c294f007..dcf44e3143 100644 --- a/src/objective-c/RxLibrary/GRXWriter.h +++ b/src/objective-c/RxLibrary/GRXWriter.h @@ -50,7 +50,7 @@ typedef NS_ENUM(NSInteger, GRXWriterState) { // The writer is temporarily paused, and won't send any more values to the // writeable unless its state is set back to Started. The writer might still // transition to the Finished state at any moment, and is allowed to send - // didFinishWithError: to its writeable. + // writesFinishedWithError: to its writeable. // // Not all implementations of writer have to support pausing, and thus // trying to set an writer's state to this value might have no effect. @@ -59,7 +59,7 @@ typedef NS_ENUM(NSInteger, GRXWriterState) { // The writer has released its writeable and won't interact with it anymore. // // One seldomly wants to set an writer's state to this value, as its - // writeable isn't notified with a didFinishWithError: message. Instead, sending + // writeable isn't notified with a writesFinishedWithError: message. Instead, sending // finishWithError: to the writer will make it notify the writeable and then // transition to this state. GRXWriterStateFinished @@ -105,7 +105,7 @@ typedef NS_ENUM(NSInteger, GRXWriterState) { // This method might only be called on writers in the NotStarted state. - (void)startWithWriteable:(id)writeable; -// Send didFinishWithError:errorOrNil immediately to the writeable, and don't send +// Send writesFinishedWithError:errorOrNil immediately to the writeable, and don't send // any more messages to it. // // This method might only be called on writers in the Started or Paused diff --git a/src/objective-c/RxLibrary/GRXWriter.m b/src/objective-c/RxLibrary/GRXWriter.m index 140284b938..cc14383560 100644 --- a/src/objective-c/RxLibrary/GRXWriter.m +++ b/src/objective-c/RxLibrary/GRXWriter.m @@ -62,7 +62,7 @@ - (void)finishOutputWithError:(NSError *)errorOrNil { id writeable = _writeable; _writeable = nil; - [writeable didFinishWithError:errorOrNil]; + [writeable writesFinishedWithError:errorOrNil]; } // This is used to stop the input writer. It nillifies our reference to it @@ -79,7 +79,7 @@ [_writeable writeValue:value]; } -- (void)didFinishWithError:(NSError *)errorOrNil { +- (void)writesFinishedWithError:(NSError *)errorOrNil { _writer = nil; [self finishOutputWithError:errorOrNil]; } diff --git a/src/objective-c/examples/Sample/SampleTests/RemoteProtoTests.m b/src/objective-c/examples/Sample/SampleTests/RemoteProtoTests.m index 51965a1dd7..7e063fddb4 100644 --- a/src/objective-c/examples/Sample/SampleTests/RemoteProtoTests.m +++ b/src/objective-c/examples/Sample/SampleTests/RemoteProtoTests.m @@ -227,7 +227,7 @@ requestedResponseSize:responses[index]]; [requestsBuffer writeValue:request]; } else { - [requestsBuffer didFinishWithError:nil]; + [requestsBuffer writesFinishedWithError:nil]; } } -- cgit v1.2.3 From c5c65aae2ed3db08811efa477f7467aa1ed71936 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Tue, 19 May 2015 22:40:02 -0700 Subject: Add instructions to generate code and integrate it --- src/objective-c/README.md | 48 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) (limited to 'src/objective-c') diff --git a/src/objective-c/README.md b/src/objective-c/README.md index 05e9f2b4dc..cdba3777dd 100644 --- a/src/objective-c/README.md +++ b/src/objective-c/README.md @@ -1,3 +1,47 @@ -gRPC implementation for Objective-C on iOS +# gRPC for Objective-C -This is a work in progress. +## How to generate a client library from a Protocol Buffers definition + +First install v3 of the Protocol Buffers compiler (_protoc_), by cloning [its Git repository](https://github.com/google/protobuf) and following these [installation instructions](https://github.com/google/protobuf#c-installation---unix) (the ones titled C++; don't miss the note for Mac users). + +Then clone this repository and execute the following commands from the root directory where it was cloned. + +Compile the gRPC plugins for _protoc_: +```sh +make plugins +``` + +Create a symbolic link to the compiled plugin binary somewhere in your `$PATH`: +```sh +ln -s `pwd`/bins/opt/grpc_objective_c_plugin /usr/local/bin/protoc-gen-objcgrpc +``` +(Notice that the name of the created link must begin with "protoc-gen-" for _protoc_ to recognize it as a plugin). + +If you don't want to create the symbolic link, you can alternatively copy the binary (with the appropriate name). Or you might prefer instead to specify the plugin's path as a flag when invoking _protoc_, in which case no system modification nor renaming is necessary. + +Finally, run _protoc_ with the following flags to generate the client library for your `.proto` files: + +```sh +protoc --objc_out=. --objcrpc_out=. *.proto +``` + +This will generate a pair of `.pbobjc.h`/`.pbobjc.m` files for each `.proto` file, with the messages and enums defined in them. And a pair of `.pbrpc.h`/`.pbrpc.m` files for each `.proto` file with services defined. The latter contains the code to make remote calls to the specified API. + +## How to integrate a generated gRPC library in your project + +### If you use Cocoapods + +This is the recommended approach. + +You need to create a Podspec file for the generated library. This is simply a matter of copying an example like [this one](https://github.com/grpc/grpc/blob/master/src/objective-c/examples/Sample/RemoteTestClient/RemoteTest.podspec) to the directory where the source files were generated. Update the name and other metadata of the Podspec as suitable. + +Once your library has a Podspec, refer to it from your Podfile using `:path` as described [here](https://guides.cocoapods.org/using/the-podfile.html#using-the-files-from-a-folder-local-to-the-machine). + +### If you don't use Cocoapods + +You need to compile the generated `.pbpbjc.*` files (the enums and messages) without ARC support, and the generated `.pbrpc.*` files (the services) with ARC support. The generated code depends on v0.3+ of the Objective-C gRPC runtime library and v3.0+ of the Objective-C Protobuf runtime library. + +These libraries need to be integrated into your project as described in their respective Podspec files: + +* [Podspec](https://github.com/grpc/grpc/blob/master/gRPC.podspec) for the Objective-C gRPC runtime library. This can be tedious to configure manually. +* [Podspec](https://github.com/jcanizales/protobuf/blob/add-podspec/Protobuf.podspec) for the Objective-C Protobuf runtime library. -- cgit v1.2.3