reconnect.h (3712B)
1 // Copyright (c) 2020 Cloudflare, Inc. and contributors 2 // Licensed under the MIT License: 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 #pragma once 23 24 #include "capability.h" 25 #include <kj/function.h> 26 27 CAPNP_BEGIN_HEADER 28 29 namespace capnp { 30 31 template <typename ConnectFunc> 32 auto autoReconnect(ConnectFunc&& connect); 33 // Creates a capability that reconstructs itself every time it becomes disconnected. 34 // 35 // `connect()` is a function which is invoked to initially construct the capability, and then 36 // invoked again each time the capability is found to be disconnected. `connect()` may return 37 // any capability `Client` type. 38 // 39 // Example usage might look like: 40 // 41 // Foo::Client foo = autoReconnect([&rpcSystem, vatId]() { 42 // return rpcSystem.bootstrap(vatId).castAs<RootType>().getFooRequest().send().getFoo(); 43 // }); 44 // 45 // The given function is initially called synchronously, and the returned `foo` is a wrapper 46 // around what the function returned. But any time this capability becomes disconnected, the 47 // function is invoked again, and future calls are directed to the new result. 48 // 49 // Any call that is in-flight when the capability becomes disconnected still fails with a 50 // DISCONNECTED exception. The caller should respond by retrying, as a retry will target the 51 // newly-reconnected capability. However, the caller should limit the number of times it retries, 52 // to avoid an infinite loop in the case that the DISCONNECTED exception actually represents a 53 // permanent problem. Consider using `kj::retryOnDisconnect()` to implement this behavior. 54 55 template <typename ConnectFunc> 56 auto lazyAutoReconnect(ConnectFunc&& connect); 57 // The same as autoReconnect, but doesn't call the provided connect function until the first 58 // time the capability is used. Note that only the initial connection is lazy -- upon 59 // disconnected errors this will still reconnect eagerly. 60 61 // ======================================================================================= 62 // inline implementation details 63 64 Capability::Client autoReconnect(kj::Function<Capability::Client()> connect); 65 template <typename ConnectFunc> 66 auto autoReconnect(ConnectFunc&& connect) { 67 return autoReconnect(kj::Function<Capability::Client()>(kj::fwd<ConnectFunc>(connect))) 68 .castAs<FromClient<kj::Decay<decltype(connect())>>>(); 69 } 70 71 Capability::Client lazyAutoReconnect(kj::Function<Capability::Client()> connect); 72 template <typename ConnectFunc> 73 auto lazyAutoReconnect(ConnectFunc&& connect) { 74 return lazyAutoReconnect(kj::Function<Capability::Client()>(kj::fwd<ConnectFunc>(connect))) 75 .castAs<FromClient<kj::Decay<decltype(connect())>>>(); 76 } 77 78 } // namespace capnp 79 80 CAPNP_END_HEADER