1 // Copyright 2021 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <cstdint> 17 18 #include "pw_bytes/span.h" 19 #include "pw_function/function.h" 20 #include "pw_rpc/internal/call.h" 21 #include "pw_rpc/internal/endpoint.h" 22 #include "pw_rpc/internal/lock.h" 23 24 namespace pw::rpc::internal { 25 26 // A Call object, as used by an RPC client. 27 class ClientCall : public Call { 28 public: id()29 uint32_t id() const PW_LOCKS_EXCLUDED(rpc_lock()) { 30 RpcLockGuard lock; 31 return Call::id(); 32 } 33 34 protected: 35 // Initializes CallProperties for a struct-based client call impl. StructCallProps(MethodType type)36 static constexpr CallProperties StructCallProps(MethodType type) { 37 return CallProperties(type, kClientCall, kProtoStruct); 38 } 39 40 // Initializes CallProperties for a raw client call. RawCallProps(MethodType type)41 static constexpr CallProperties RawCallProps(MethodType type) { 42 return CallProperties(type, kClientCall, kRawProto); 43 } 44 45 constexpr ClientCall() = default; 46 ClientCall(LockedEndpoint & client,uint32_t channel_id,uint32_t service_id,uint32_t method_id,CallProperties properties)47 ClientCall(LockedEndpoint& client, 48 uint32_t channel_id, 49 uint32_t service_id, 50 uint32_t method_id, 51 CallProperties properties) PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) 52 : Call(client, channel_id, service_id, method_id, properties) {} 53 ~ClientCall()54 ~ClientCall() { DestroyClientCall(); } 55 56 // Public function that closes a call client-side without cancelling it on the 57 // server. Abandon()58 void Abandon() PW_LOCKS_EXCLUDED(rpc_lock()) { 59 RpcLockGuard lock; 60 CloseClientCall(); 61 } 62 CloseAndWaitForCallbacks()63 void CloseAndWaitForCallbacks() { DestroyClientCall(); } 64 65 void MoveClientCallFrom(ClientCall& other) 66 PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()); 67 }; 68 69 // Unary response client calls receive both a payload and the status in their 70 // on_completed callback. The on_next callback is not used. 71 class UnaryResponseClientCall : public ClientCall { 72 public: ~UnaryResponseClientCall()73 ~UnaryResponseClientCall() { DestroyClientCall(); } 74 75 // Start call for raw unary response RPCs. 76 template <typename CallType> Start(Endpoint & client,uint32_t channel_id,uint32_t service_id,uint32_t method_id,Function<void (ConstByteSpan,Status)> && on_completed,Function<void (Status)> && on_error,ConstByteSpan request)77 static CallType Start(Endpoint& client, 78 uint32_t channel_id, 79 uint32_t service_id, 80 uint32_t method_id, 81 Function<void(ConstByteSpan, Status)>&& on_completed, 82 Function<void(Status)>&& on_error, 83 ConstByteSpan request) PW_LOCKS_EXCLUDED(rpc_lock()) { 84 rpc_lock().lock(); 85 CallType call(client.ClaimLocked(), channel_id, service_id, method_id); 86 call.set_on_completed_locked(std::move(on_completed)); 87 call.set_on_error_locked(std::move(on_error)); 88 89 call.SendInitialClientRequest(request); 90 client.CleanUpCalls(); 91 return call; 92 } 93 94 void HandleCompleted(ConstByteSpan response, Status status) 95 PW_UNLOCK_FUNCTION(rpc_lock()); 96 97 protected: 98 constexpr UnaryResponseClientCall() = default; 99 UnaryResponseClientCall(LockedEndpoint & client,uint32_t channel_id,uint32_t service_id,uint32_t method_id,CallProperties properties)100 UnaryResponseClientCall(LockedEndpoint& client, 101 uint32_t channel_id, 102 uint32_t service_id, 103 uint32_t method_id, 104 CallProperties properties) 105 PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) 106 : ClientCall(client, channel_id, service_id, method_id, properties) {} 107 UnaryResponseClientCall(UnaryResponseClientCall && other)108 UnaryResponseClientCall(UnaryResponseClientCall&& other) { 109 *this = std::move(other); 110 } 111 112 UnaryResponseClientCall& operator=(UnaryResponseClientCall&& other) PW_LOCKS_EXCLUDED(rpc_lock ())113 PW_LOCKS_EXCLUDED(rpc_lock()) { 114 RpcLockGuard lock; 115 MoveUnaryResponseClientCallFrom(other); 116 return *this; 117 } 118 MoveUnaryResponseClientCallFrom(UnaryResponseClientCall & other)119 void MoveUnaryResponseClientCallFrom(UnaryResponseClientCall& other) 120 PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) { 121 MoveClientCallFrom(other); 122 on_completed_ = std::move(other.on_completed_); 123 } 124 set_on_completed(Function<void (ConstByteSpan,Status)> && on_completed)125 void set_on_completed(Function<void(ConstByteSpan, Status)>&& on_completed) 126 PW_LOCKS_EXCLUDED(rpc_lock()) { 127 RpcLockGuard lock; 128 set_on_completed_locked(std::move(on_completed)); 129 } 130 set_on_completed_locked(Function<void (ConstByteSpan,Status)> && on_completed)131 void set_on_completed_locked( 132 Function<void(ConstByteSpan, Status)>&& on_completed) 133 PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) { 134 on_completed_ = std::move(on_completed); 135 } 136 137 private: 138 using internal::ClientCall::set_on_next; // Not used in unary response calls. 139 140 Function<void(ConstByteSpan, Status)> on_completed_ PW_GUARDED_BY(rpc_lock()); 141 }; 142 143 // Stream response client calls only receive the status in their on_completed 144 // callback. Payloads are sent through the on_next callback. 145 class StreamResponseClientCall : public ClientCall { 146 public: ~StreamResponseClientCall()147 ~StreamResponseClientCall() { DestroyClientCall(); } 148 149 // Start call for raw stream response RPCs. 150 template <typename CallType> Start(Endpoint & client,uint32_t channel_id,uint32_t service_id,uint32_t method_id,Function<void (ConstByteSpan)> && on_next,Function<void (Status)> && on_completed,Function<void (Status)> && on_error,ConstByteSpan request)151 static CallType Start(Endpoint& client, 152 uint32_t channel_id, 153 uint32_t service_id, 154 uint32_t method_id, 155 Function<void(ConstByteSpan)>&& on_next, 156 Function<void(Status)>&& on_completed, 157 Function<void(Status)>&& on_error, 158 ConstByteSpan request) PW_LOCKS_EXCLUDED(rpc_lock()) { 159 rpc_lock().lock(); 160 CallType call(client.ClaimLocked(), channel_id, service_id, method_id); 161 162 call.set_on_next_locked(std::move(on_next)); 163 call.set_on_completed_locked(std::move(on_completed)); 164 call.set_on_error_locked(std::move(on_error)); 165 166 call.SendInitialClientRequest(request); 167 client.CleanUpCalls(); 168 return call; 169 } 170 171 void HandleCompleted(Status status) PW_UNLOCK_FUNCTION(rpc_lock()); 172 173 protected: 174 constexpr StreamResponseClientCall() = default; 175 StreamResponseClientCall(LockedEndpoint & client,uint32_t channel_id,uint32_t service_id,uint32_t method_id,CallProperties properties)176 StreamResponseClientCall(LockedEndpoint& client, 177 uint32_t channel_id, 178 uint32_t service_id, 179 uint32_t method_id, 180 CallProperties properties) 181 PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) 182 : ClientCall(client, channel_id, service_id, method_id, properties) {} 183 StreamResponseClientCall(StreamResponseClientCall && other)184 StreamResponseClientCall(StreamResponseClientCall&& other) { 185 *this = std::move(other); 186 } 187 188 StreamResponseClientCall& operator=(StreamResponseClientCall&& other) PW_LOCKS_EXCLUDED(rpc_lock ())189 PW_LOCKS_EXCLUDED(rpc_lock()) { 190 RpcLockGuard lock; 191 MoveStreamResponseClientCallFrom(other); 192 return *this; 193 } 194 MoveStreamResponseClientCallFrom(StreamResponseClientCall & other)195 void MoveStreamResponseClientCallFrom(StreamResponseClientCall& other) 196 PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) { 197 MoveClientCallFrom(other); 198 on_completed_ = std::move(other.on_completed_); 199 } 200 set_on_completed(Function<void (Status)> && on_completed)201 void set_on_completed(Function<void(Status)>&& on_completed) 202 PW_LOCKS_EXCLUDED(rpc_lock()) { 203 RpcLockGuard lock; 204 set_on_completed_locked(std::move(on_completed)); 205 } 206 set_on_completed_locked(Function<void (Status)> && on_completed)207 void set_on_completed_locked(Function<void(Status)>&& on_completed) 208 PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) { 209 on_completed_ = std::move(on_completed); 210 } 211 212 private: 213 Function<void(Status)> on_completed_ PW_GUARDED_BY(rpc_lock()); 214 }; 215 216 } // namespace pw::rpc::internal 217