Line data Source code
1 : /* 2 : * Copyright (c) 2021 Project CHIP Authors 3 : * 4 : * Licensed under the Apache License, Version 2.0 (the "License"); 5 : * you may not use this file except in compliance with the License. 6 : * You may obtain a copy of the License at 7 : * 8 : * http://www.apache.org/licenses/LICENSE-2.0 9 : * 10 : * Unless required by applicable law or agreed to in writing, software 11 : * distributed under the License is distributed on an "AS IS" BASIS, 12 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 : * See the License for the specific language governing permissions and 14 : * limitations under the License. 15 : */ 16 : 17 : #pragma once 18 : 19 : #include <lib/core/Optional.h> 20 : #include <lib/support/IntrusiveList.h> 21 : #include <messaging/ExchangeContext.h> 22 : 23 : #ifndef CHIP_EXCHANGE_HOLDER_DETAIL_LOGGING 24 : #define CHIP_EXCHANGE_HOLDER_DETAIL_LOGGING 0 25 : #endif // CHIP_EXCHANGE_HOLDER_DETAIL_LOGGING 26 : 27 : namespace chip { 28 : namespace Messaging { 29 : 30 : /** 31 : * @brief 32 : * This provides a RAII'fied wrapper for an ExchangeContext that automatically manages 33 : * cleaning up the EC when the holder ceases to exist, or acquires a new exchange. This is 34 : * meant to be used by application and protocol logic code that would otherwise need to closely 35 : * manage their internal pointers to an ExchangeContext and correctly 36 : * null-it out/abort it depending on the circumstances. This relies on clear rules 37 : * established by ExchangeContext and the transfer of ownership at various points 38 : * in its lifetime. 39 : * 40 : * An equivalent but simplified version of the rules around exchange management as specified in 41 : * ExchangeDelegate.h are provided here for consumers: 42 : * 43 : * 1. When an exchange is allocated, the holder takes over ownership of the exchange when Grab() is invoked. 44 : * Until a message is sent successfully, the holder will automatically manage the exchange until its 45 : * destructor or Release() is invoked. 46 : * 47 : * 2. If you send a message successfully that doesn't require a response, invoking Get() on the holder there-after will return 48 : * nullptr. 49 : * 50 : * 3. If you send a message successfully that does require a response, invoking Get() on the holder will return a valid 51 : * pointer until the response is received or times out. 52 : * 53 : * 4. On reception of a message on an exchange, if you return from OnMessageReceived() and no messages were sent on that exchange, 54 : * invoking Get() on the holder will return a nullptr. 55 : * 56 : * 5. If you invoke WillSendMessage() on the exchange in your implementation of OnMessageReceived indicating a desire to send a 57 : * message later on the exchange, invoking Get() on the holder will return a valid exchange until SendMessage() on the exchange 58 : * is called, at which point, rules 2 and 3 apply. 59 : * 60 : * 6. This is a delegate forwarder - consumers can still register to be an ExchangeDelegate 61 : * and get notified of all relevant happenings on that delegate interface. 62 : * 63 : * 7. At no point shall you call Abort/Close/Release/Retain on the exchange tracked by the holder. 64 : * 65 : */ 66 : class ExchangeHolder : public ExchangeDelegate 67 : { 68 : public: 69 : /** 70 : * @brief 71 : * Constructor that takes an ExchangeDelegate that is forwarded all relevant 72 : * calls from the underlying exchange. 73 : */ 74 2135 : ExchangeHolder(ExchangeDelegate & delegate) : mpExchangeDelegate(delegate) {} 75 : 76 2076 : virtual ~ExchangeHolder() 77 2076 : { 78 : #if CHIP_EXCHANGE_HOLDER_DETAIL_LOGGING 79 : ChipLogDetail(ExchangeManager, "[%p] ~ExchangeHolder", this); 80 : #endif 81 2076 : Release(); 82 2076 : } 83 : 84 : bool Contains(const ExchangeContext * exchange) const { return mpExchangeCtx == exchange; } 85 : 86 : /** 87 : * @brief 88 : * Replaces the held exchange and associated delegate to instead track the given ExchangeContext, aborting 89 : * and dereferencing any previously held exchange as necessary. This method should be called whenever protocol logic 90 : * that is managing this holder is transitioning from an outdated Exchange to a new one, often during 91 : * the start of a new transaction. 92 : */ 93 2701 : void Grab(ExchangeContext * exchange) 94 : { 95 2701 : VerifyOrDie(exchange != nullptr); 96 : 97 2701 : Release(); 98 : 99 2701 : mpExchangeCtx = exchange; 100 2701 : mpExchangeCtx->SetDelegate(this); 101 : 102 : #if CHIP_EXCHANGE_HOLDER_DETAIL_LOGGING 103 : ChipLogDetail(ExchangeManager, "[%p] ExchangeHolder::Grab: Acquired EC %p", this, exchange); 104 : #endif 105 2701 : } 106 : 107 : /* 108 : * @brief 109 : * This shuts down the exchange (if a valid one is being tracked) and releases our reference to it. 110 : */ 111 4526 : void Release() 112 : { 113 : #if CHIP_EXCHANGE_HOLDER_DETAIL_LOGGING 114 : ChipLogDetail(ExchangeManager, "[%p] ExchangeHolder::Release: mpExchangeCtx = %p", this, mpExchangeCtx); 115 : #endif 116 : 117 4526 : if (mpExchangeCtx) 118 : { 119 742 : mpExchangeCtx->SetDelegate(nullptr); 120 : 121 : /** 122 : * Shutting down the exchange requires calling Abort() on the exchange selectively in the following scenarios: 123 : * 1. The exchange is currently awaiting a response. This would have happened if our consumer just sent a message 124 : * on the exchange and is awaiting a response. Since we no longer care to wait for the response, we don't care about 125 : * doing MRP retries for the send we just did, so abort the exchange. 126 : * 127 : * 2. Our consumer has signaled an interest in sending a message. This could have been signaled right at exchange 128 : * creation time as the initiator, or when handling a message and the consumer intends to send a response, albeit, 129 : * asynchronously. In both cases, the stack expects the exchange consumer to close/abort the EC if it no longer has 130 : * interest in it. Since we don't have a pending message at this point, calling Abort is OK here as well. 131 : * 132 : */ 133 742 : if (mpExchangeCtx->IsResponseExpected() || mpExchangeCtx->IsSendExpected()) 134 : { 135 : #if CHIP_EXCHANGE_HOLDER_DETAIL_LOGGING 136 : ChipLogDetail(ExchangeManager, "[%p] ExchangeHolder::Release: Aborting!", this); 137 : #endif 138 98 : mpExchangeCtx->Abort(); 139 : } 140 : } 141 : 142 4526 : mpExchangeCtx = nullptr; 143 4526 : } 144 : 145 6174 : explicit operator bool() const { return mpExchangeCtx != nullptr; } 146 4347 : ExchangeContext * Get() const { return mpExchangeCtx; } 147 : 148 22328 : ExchangeContext * operator->() const 149 : { 150 22328 : VerifyOrDie(mpExchangeCtx != nullptr); 151 22328 : return mpExchangeCtx; 152 : } 153 : 154 : private: 155 3127 : CHIP_ERROR OnMessageReceived(ExchangeContext * ec, const PayloadHeader & payloadHeader, 156 : System::PacketBufferHandle && payload) override 157 : { 158 3127 : return mpExchangeDelegate.OnMessageReceived(ec, payloadHeader, std::move(payload)); 159 : } 160 : 161 19 : void OnResponseTimeout(ExchangeContext * ec) override { return mpExchangeDelegate.OnResponseTimeout(ec); } 162 : 163 1333 : void OnExchangeClosing(ExchangeContext * ec) override 164 : { 165 : #if CHIP_EXCHANGE_HOLDER_DETAIL_LOGGING 166 : ChipLogDetail(ExchangeManager, "[%p] ExchangeHolder::OnExchangeClosing: mpExchangeCtx: %p", this, mpExchangeCtx); 167 : #endif 168 : 169 1333 : if (mpExchangeCtx) 170 : { 171 1333 : mpExchangeCtx->SetDelegate(nullptr); 172 : 173 : /** 174 : * Unless our consumer has signalled an intention to send a message in the future, the exchange 175 : * is owned by the exchange layer and it will automatically handle releasing the ref. So, just null 176 : * out our reference to it. 177 : */ 178 1333 : if (!mpExchangeCtx->IsSendExpected()) 179 : { 180 : #if CHIP_EXCHANGE_HOLDER_DETAIL_LOGGING 181 : ChipLogDetail(ExchangeManager, "[%p] ExchangeHolder::OnExchangeClosing: nulling out ref...", this); 182 : #endif 183 1333 : mpExchangeCtx = nullptr; 184 : } 185 : } 186 : 187 1333 : mpExchangeDelegate.OnExchangeClosing(ec); 188 1333 : } 189 : 190 0 : ExchangeMessageDispatch & GetMessageDispatch() override { return mpExchangeDelegate.GetMessageDispatch(); } 191 : 192 : ExchangeDelegate & mpExchangeDelegate; 193 : ExchangeContext * mpExchangeCtx = nullptr; 194 : }; 195 : 196 : } // namespace Messaging 197 : } // namespace chip