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 2414 : ExchangeHolder(ExchangeDelegate & delegate) : mpExchangeDelegate(delegate) {}
75 :
76 2414 : virtual ~ExchangeHolder()
77 2414 : {
78 : #if CHIP_EXCHANGE_HOLDER_DETAIL_LOGGING
79 : ChipLogDetail(ExchangeManager, "[%p] ~ExchangeHolder", this);
80 : #endif
81 2414 : Release();
82 2414 : }
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 2892 : void Grab(ExchangeContext * exchange)
94 : {
95 2892 : VerifyOrDie(exchange != nullptr);
96 :
97 2892 : Release();
98 :
99 2892 : mpExchangeCtx = exchange;
100 2892 : 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 2892 : }
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 5117 : void Release()
112 : {
113 : #if CHIP_EXCHANGE_HOLDER_DETAIL_LOGGING
114 : ChipLogDetail(ExchangeManager, "[%p] ExchangeHolder::Release: mpExchangeCtx = %p", this, mpExchangeCtx);
115 : #endif
116 :
117 5117 : if (mpExchangeCtx)
118 : {
119 830 : 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 830 : if (mpExchangeCtx->IsResponseExpected() || mpExchangeCtx->IsSendExpected())
134 : {
135 : #if CHIP_EXCHANGE_HOLDER_DETAIL_LOGGING
136 : ChipLogDetail(ExchangeManager, "[%p] ExchangeHolder::Release: Aborting!", this);
137 : #endif
138 100 : mpExchangeCtx->Abort();
139 : }
140 : }
141 :
142 5117 : mpExchangeCtx = nullptr;
143 5117 : }
144 :
145 6266 : explicit operator bool() const { return mpExchangeCtx != nullptr; }
146 4322 : ExchangeContext * Get() const { return mpExchangeCtx; }
147 :
148 22970 : ExchangeContext * operator->() const
149 : {
150 22970 : VerifyOrDie(mpExchangeCtx != nullptr);
151 22970 : return mpExchangeCtx;
152 : }
153 :
154 : private:
155 3210 : CHIP_ERROR OnMessageReceived(ExchangeContext * ec, const PayloadHeader & payloadHeader,
156 : System::PacketBufferHandle && payload) override
157 : {
158 3210 : return mpExchangeDelegate.OnMessageReceived(ec, payloadHeader, std::move(payload));
159 : }
160 :
161 26 : void OnResponseTimeout(ExchangeContext * ec) override { return mpExchangeDelegate.OnResponseTimeout(ec); }
162 :
163 1432 : 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 1432 : if (mpExchangeCtx)
170 : {
171 1432 : 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 1432 : if (!mpExchangeCtx->IsSendExpected())
179 : {
180 : #if CHIP_EXCHANGE_HOLDER_DETAIL_LOGGING
181 : ChipLogDetail(ExchangeManager, "[%p] ExchangeHolder::OnExchangeClosing: nulling out ref...", this);
182 : #endif
183 1431 : mpExchangeCtx = nullptr;
184 : }
185 : }
186 :
187 1432 : mpExchangeDelegate.OnExchangeClosing(ec);
188 1432 : }
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
|