From 5e6723203013b365c2a60158578f73abf480b8bf Mon Sep 17 00:00:00 2001
From: Jorge Canizales <jcanizales@google.com>
Date: Wed, 9 Mar 2016 13:34:28 -0800
Subject: [PATCH] Make Writeable with single handler robust against stream
 Writers

---
 src/objective-c/RxLibrary/GRXWriteable.m | 37 ++++++++++++++++++++----
 1 file changed, 32 insertions(+), 5 deletions(-)

diff --git a/src/objective-c/RxLibrary/GRXWriteable.m b/src/objective-c/RxLibrary/GRXWriteable.m
index 2729d62b72..b122c04ce0 100644
--- a/src/objective-c/RxLibrary/GRXWriteable.m
+++ b/src/objective-c/RxLibrary/GRXWriteable.m
@@ -42,11 +42,38 @@
   if (!handler) {
     return [[self alloc] init];
   }
-  return [[self alloc] initWithValueHandler:^(id value) {
-    handler(value, nil);
-  } completionHandler:^(NSError *errorOrNil) {
-    if (errorOrNil) {
-      handler(nil, errorOrNil);
+  // We nilify this variable when the block is invoked, so that handler is only invoked once even if
+  // the writer tries to write multiple values.
+  __block GRXEventHandler eventHandler = ^(BOOL done, id value, NSError *error) {
+    // Nillify eventHandler before invoking handler, in case the latter causes the former to be
+    // executed recursively. Because blocks can be deallocated even during execution, we have to
+    // first retain handler locally to guarantee it's valid.
+    // TODO(jcanizales): Just turn this craziness into a simple subclass of GRXWriteable.
+    GRXSingleHandler singleHandler = handler;
+    eventHandler = nil;
+
+    if (value) {
+      singleHandler(value, nil);
+    } else if (error) {
+      singleHandler(nil, error);
+    } else {
+      NSDictionary *userInfo = @{
+        NSLocalizedDescriptionKey: @"The writer finished without producing any value."
+      };
+      // Even though RxLibrary is independent of gRPC, the domain and code here are, for the moment,
+      // set to the values of kGRPCErrorDomain and GRPCErrorCodeInternal. This way, the error formed
+      // is the one user of gRPC would expect if the server failed to produce a response.
+      //
+      // TODO(jcanizales): Figure out a way to keep errors of RxLibrary generic without making users
+      // of gRPC take care of two different error domains and error code enums. A possibility is to
+      // add error handling to GRXWriters or GRXWriteables, and use them to translate errors between
+      // the two domains.
+      singleHandler(nil, [NSError errorWithDomain:@"io.grpc" code:13 userInfo:userInfo]);
+    }
+  };
+  return [self writeableWithEventHandler:^(BOOL done, id value, NSError *error) {
+    if (eventHandler) {
+      eventHandler(done, value, error);
     }
   }];
 }
-- 
GitLab