pw_rpc: Cancel streaming RPCs when aborted

Cancel server streaming RPCs if they time out or if iteration is
aborted by an exception (such as KeyboardInterrupt).

Change-Id: Ib6a29c1456e600937129830fdfa2cabeb76d2aca
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/39323
Reviewed-by: Alexei Frolov <frolv@google.com>
Commit-Queue: Wyatt Hepler <hepler@google.com>
diff --git a/pw_rpc/py/pw_rpc/callback_client.py b/pw_rpc/py/pw_rpc/callback_client.py
index 7e71420..4c0d27d 100644
--- a/pw_rpc/py/pw_rpc/callback_client.py
+++ b/pw_rpc/py/pw_rpc/callback_client.py
@@ -205,6 +205,9 @@
     def method(self) -> Method:
         return self._method_client.method
 
+    def cancel(self) -> None:
+        self._method_client._rpcs.send_cancel(self._method_client._rpc)  # pylint: disable=protected-access
+
     def responses(self,
                   *,
                   block: bool = True,
@@ -230,9 +233,11 @@
 
                 yield response
         except queue.Empty:
-            pass
-
-        raise RpcTimeout(self._method_client._rpc, timeout_s)  # pylint: disable=protected-access
+            self.cancel()
+            raise RpcTimeout(self._method_client._rpc, timeout_s)  # pylint: disable=protected-access
+        except:
+            self.cancel()  # pylint: disable=protected-access
+            raise
 
     def __iter__(self):
         return self.responses()