blob: fb7e2cd39c55bdc7e69e0e8dce994dde50c08119 [file] [log] [blame]
/*
* Copyright (c) 2021 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <lib/core/Optional.h>
#include <lib/support/IntrusiveList.h>
#include <messaging/ExchangeContext.h>
namespace chip {
namespace Messaging {
/**
* @brief
* This provides a RAII'fied wrapper for an ExchangeContext that automatically manages
* cleaning up the EC when the holder ceases to exist, or acquires a new exchange. This is
* meant to be used by application and protocol logic code that would otherwise need to closely
* manage their internal pointers to an ExchangeContext and correctly
* null-it out/abort it depending on the circumstances. This relies on clear rules
* established by ExchangeContext and the transfer of ownership at various points
* in its lifetime.
*
* It does this by intercepting OnExchangeClosing and looking at the various
* states the exchange might be in to decide how best to correctly shutdown the exchange.
* (see AbortIfNeeded()).
*
* This is a delegate forwarder - consumers can still register to be an ExchangeDelegate
* and get notified of all relevant happenings on that delegate interface.
*
*/
class ExchangeHolder : public ExchangeDelegate
{
public:
/**
* @brief
* Constructor that takes an ExchangeDelegate that is forwarded all relevant
* calls from the underlying exchange.
*/
ExchangeHolder(ExchangeDelegate & delegate) : mpExchangeDelegate(delegate) {}
virtual ~ExchangeHolder() { Release(); }
bool Contains(const ExchangeContext * exchange) const { return mpExchangeCtx == exchange; }
/**
* @brief
* Replaces the held exchange and associated delegate to instead track the given ExchangeContext, aborting
* and dereferencing any previously held exchange as necessary. This method should be called whenever protocol logic
* that is managing this holder is transitioning from an outdated Exchange to a new one, often during
* the start of a new transaction.
*/
void Grab(ExchangeContext * exchange)
{
VerifyOrDie(exchange != nullptr);
Release();
mpExchangeCtx = exchange;
mpExchangeCtx->SetDelegate(this);
}
/*
* @brief
* This shuts down the exchange (if a valid one is being tracked) and releases our reference to it.
*/
void Release()
{
if (mpExchangeCtx)
{
mpExchangeCtx->SetDelegate(nullptr);
/**
* Shutting down the exchange requires calling Abort() on the exchange selectively in the following scenarios:
* 1. The exchange is currently awaiting a response. This would have happened if our consumer just sent a message
* on the exchange and is awaiting a response. Since we no longer care to wait for the response, we don't care about
* doing MRP retries for the send we just did, so abort the exchange.
*
* 2. Our consumer has signaled an interest in sending a message. This could have been signaled right at exchange
* creation time as the initiator, or when handling a message and the consumer intends to send a response, albeit,
* asynchronously. In both cases, the stack expects the exchange consumer to close/abort the EC if it no longer has
* interest in it. Since we don't have a pending message at this point, calling Abort is OK here as well.
*
*/
if (mpExchangeCtx->IsResponseExpected() || mpExchangeCtx->IsSendExpected())
{
mpExchangeCtx->Abort();
}
}
mpExchangeCtx = nullptr;
}
explicit operator bool() const { return mpExchangeCtx != nullptr; }
ExchangeContext * Get() const { return mpExchangeCtx; }
ExchangeContext * operator->() const
{
VerifyOrDie(mpExchangeCtx != nullptr);
return mpExchangeCtx;
}
private:
CHIP_ERROR OnMessageReceived(ExchangeContext * ec, const PayloadHeader & payloadHeader,
System::PacketBufferHandle && payload) override
{
return mpExchangeDelegate.OnMessageReceived(ec, payloadHeader, std::move(payload));
}
void OnResponseTimeout(ExchangeContext * ec) override { return mpExchangeDelegate.OnResponseTimeout(ec); }
void OnExchangeClosing(ExchangeContext * ec) override
{
if (mpExchangeCtx)
{
mpExchangeCtx->SetDelegate(nullptr);
mpExchangeCtx = nullptr;
}
mpExchangeDelegate.OnExchangeClosing(ec);
}
ExchangeMessageDispatch & GetMessageDispatch() override { return mpExchangeDelegate.GetMessageDispatch(); }
ExchangeDelegate & mpExchangeDelegate;
ExchangeContext * mpExchangeCtx = nullptr;
};
} // namespace Messaging
} // namespace chip