Move CommandSender/Handler and WriteClient/Handler over to `ExchangeHolder` (#21081)
* Move CommandSender/Handler and WriteClient/Handler over to using the new
but safer, ExchangeHolder way of EC management.
* Update src/app/CommandHandler.h
diff --git a/src/app/CommandHandler.cpp b/src/app/CommandHandler.cpp
index 2fc2dc5..d17c4f3 100644
--- a/src/app/CommandHandler.cpp
+++ b/src/app/CommandHandler.cpp
@@ -40,7 +40,7 @@
namespace chip {
namespace app {
-CommandHandler::CommandHandler(Callback * apCallback) : mpCallback(apCallback), mSuppressResponse(false) {}
+CommandHandler::CommandHandler(Callback * apCallback) : mExchangeCtx(*this), mpCallback(apCallback), mSuppressResponse(false) {}
CHIP_ERROR CommandHandler::AllocateBuffer()
{
@@ -73,7 +73,7 @@
// NOTE: we already know this is an InvokeCommand Request message because we explicitly registered with the
// Exchange Manager for unsolicited InvokeCommand Requests.
- mpExchangeCtx = ec;
+ mExchangeCtx.Grab(ec);
// Use the RAII feature, if this is the only Handle when this function returns, DecrementHoldOff will trigger sending response.
// TODO: This is broken! If something under here returns error, we will try
@@ -81,7 +81,7 @@
// response too. Figure out at what point it's our responsibility to
// handler errors vs our caller's.
Handle workHandle(this);
- mpExchangeCtx->WillSendMessage();
+ mExchangeCtx->WillSendMessage();
ReturnErrorOnFailure(ProcessInvokeRequest(std::move(payload), isTimedInvoke));
return CHIP_NO_ERROR;
@@ -103,26 +103,19 @@
ReturnErrorOnFailure(invokeRequestMessage.GetTimedRequest(&mTimedRequest));
ReturnErrorOnFailure(invokeRequestMessage.GetInvokeRequests(&invokeRequests));
- VerifyOrReturnError(mpExchangeCtx != nullptr, CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrReturnError(mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);
if (mTimedRequest != isTimedInvoke)
{
// The message thinks it should be part of a timed interaction but it's
// not, or vice versa. Spec says to Respond with UNSUPPORTED_ACCESS.
- err = StatusResponse::Send(Protocols::InteractionModel::Status::UnsupportedAccess, mpExchangeCtx,
+ err = StatusResponse::Send(Protocols::InteractionModel::Status::UnsupportedAccess, mExchangeCtx.Get(),
/* aExpectResponse = */ false);
-
- if (err != CHIP_NO_ERROR)
+ if (err == CHIP_NO_ERROR)
{
- // We have to manually close the exchange, because we called
- // WillSendMessage already.
- mpExchangeCtx->Close();
+ mSentStatusResponse = true;
}
- // Null out the (now-closed) exchange, so that when we try to
- // SendCommandResponse() later (when our holdoff count drops to 0) it
- // just fails and we don't double-respond.
- mpExchangeCtx = nullptr;
return err;
}
@@ -142,7 +135,7 @@
CommandDataIB::Parser commandData;
ReturnErrorOnFailure(commandData.Init(invokeRequestsReader));
- if (mpExchangeCtx->IsGroupExchangeContext())
+ if (mExchangeCtx->IsGroupExchangeContext())
{
ReturnErrorOnFailure(ProcessGroupCommandDataIB(commandData));
}
@@ -172,18 +165,6 @@
VerifyOrDieWithMsg(mPendingWork == 0, DataManagement, "CommandHandler::Close() called with %u unfinished async work items",
static_cast<unsigned int>(mPendingWork));
- // OnDone below can destroy us before we unwind all the way back into the
- // exchange code and it tries to close itself. Make sure that it doesn't
- // try to notify us that it's closing, since we will be dead.
- //
- // For more details, see #10344.
- if (mpExchangeCtx != nullptr)
- {
- mpExchangeCtx->SetDelegate(nullptr);
- }
-
- mpExchangeCtx = nullptr;
-
if (mpCallback)
{
mpCallback->OnDone(*this);
@@ -205,21 +186,12 @@
return;
}
- if (mpExchangeCtx->IsGroupExchangeContext())
- {
- mpExchangeCtx->Close();
- }
- else
+ if (!mExchangeCtx->IsGroupExchangeContext() && !mSentStatusResponse)
{
CHIP_ERROR err = SendCommandResponse();
if (err != CHIP_NO_ERROR)
{
ChipLogError(DataManagement, "Failed to send command response: %" CHIP_ERROR_FORMAT, err.Format());
- // We marked the exchange as "WillSendMessage", need to shutdown the exchange manually to avoid leaking exchanges.
- if (mpExchangeCtx != nullptr)
- {
- mpExchangeCtx->Close();
- }
}
}
@@ -232,11 +204,11 @@
VerifyOrReturnError(mPendingWork == 0, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mState == State::AddedCommand, CHIP_ERROR_INCORRECT_STATE);
- VerifyOrReturnError(mpExchangeCtx != nullptr, CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrReturnError(mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorOnFailure(Finalize(commandPacket));
ReturnErrorOnFailure(
- mpExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::InvokeCommandResponse, std::move(commandPacket)));
+ mExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::InvokeCommandResponse, std::move(commandPacket)));
// The ExchangeContext is automatically freed here, and it makes mpExchangeCtx be temporarily dangling, but in
// all cases, we are going to call Close immediately after this function, which nulls out mpExchangeCtx.
@@ -290,7 +262,7 @@
}
}
- VerifyOrExit(mpExchangeCtx != nullptr && mpExchangeCtx->HasSessionHandle(), err = CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrExit(mExchangeCtx && mExchangeCtx->HasSessionHandle(), err = CHIP_ERROR_INCORRECT_STATE);
{
Access::SubjectDescriptor subjectDescriptor = GetSubjectDescriptor();
@@ -379,7 +351,7 @@
err = commandPath.GetCommandId(&commandId);
SuccessOrExit(err);
- groupId = mpExchangeCtx->GetSessionHandle()->AsIncomingGroupSession()->GetGroupId();
+ groupId = mExchangeCtx->GetSessionHandle()->AsIncomingGroupSession()->GetGroupId();
fabric = GetAccessingFabricIndex();
ChipLogDetail(DataManagement, "Received group command for Group=%u Cluster=" ChipLogFormatMEI " Command=" ChipLogFormatMEI,
@@ -596,7 +568,7 @@
FabricIndex CommandHandler::GetAccessingFabricIndex() const
{
- return mpExchangeCtx->GetSessionHandle()->GetFabricIndex();
+ return mExchangeCtx->GetSessionHandle()->GetFabricIndex();
}
CommandHandler * CommandHandler::Handle::Get()
@@ -666,27 +638,6 @@
ChipLogDetail(DataManagement, "ICR moving to [%10.10s]", GetStateStr());
}
-void CommandHandler::Abort()
-{
- //
- // If the exchange context hasn't already been gracefully closed
- // (signaled by setting it to null), then we need to forcibly
- // tear it down.
- //
- if (mpExchangeCtx != nullptr)
- {
- // We might be a delegate for this exchange, and we don't want the
- // OnExchangeClosing notification in that case. Null out the delegate
- // to avoid that.
- //
- // TODO: This makes all sorts of assumptions about what the delegate is
- // (notice the "might" above!) that might not hold in practice. We
- // really need a better solution here....
- mpExchangeCtx->SetDelegate(nullptr);
- mpExchangeCtx->Abort();
- mpExchangeCtx = nullptr;
- }
-}
} // namespace app
} // namespace chip
diff --git a/src/app/CommandHandler.h b/src/app/CommandHandler.h
index 200feee..da198ca 100644
--- a/src/app/CommandHandler.h
+++ b/src/app/CommandHandler.h
@@ -33,7 +33,7 @@
#include <lib/support/CodeUtils.h>
#include <lib/support/DLLUtil.h>
#include <lib/support/logging/CHIPLogging.h>
-#include <messaging/ExchangeContext.h>
+#include <messaging/ExchangeHolder.h>
#include <messaging/Flags.h>
#include <protocols/Protocols.h>
#include <protocols/interaction_model/Constants.h>
@@ -46,16 +46,9 @@
namespace chip {
namespace app {
-class CommandHandler
+class CommandHandler : public Messaging::ExchangeDelegate
{
public:
- /*
- * Destructor - as part of destruction, it will abort the exchange context
- * if a valid one still exists.
- *
- * See Abort() for details on when that might occur.
- */
- virtual ~CommandHandler() { Abort(); }
class Callback
{
public:
@@ -221,11 +214,15 @@
/**
* Gets the inner exchange context object, without ownership.
*
+ * WARNING: This is dangerous, since it is directly interacting with the
+ * exchange being managed automatically by mExchangeCtx and
+ * if not done carefully, may end up with use-after-free errors.
+ *
* @return The inner exchange context, might be nullptr if no
* exchange context has been assigned or the context
* has been released.
*/
- Messaging::ExchangeContext * GetExchangeContext() const { return mpExchangeCtx; }
+ Messaging::ExchangeContext * GetExchangeContext() const { return mExchangeCtx.Get(); }
/**
* @brief Flush acks right away for a slow command
@@ -240,18 +237,35 @@
*/
void FlushAcksRightAwayOnSlowCommand()
{
- VerifyOrReturn(mpExchangeCtx != nullptr);
- auto * msgContext = mpExchangeCtx->GetReliableMessageContext();
+ VerifyOrReturn(mExchangeCtx);
+ auto * msgContext = mExchangeCtx->GetReliableMessageContext();
VerifyOrReturn(msgContext != nullptr);
msgContext->FlushAcks();
}
- Access::SubjectDescriptor GetSubjectDescriptor() const { return mpExchangeCtx->GetSessionHandle()->GetSubjectDescriptor(); }
+ Access::SubjectDescriptor GetSubjectDescriptor() const { return mExchangeCtx->GetSessionHandle()->GetSubjectDescriptor(); }
private:
friend class TestCommandInteraction;
friend class CommandHandler::Handle;
+ CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader,
+ System::PacketBufferHandle && payload) override
+ {
+ //
+ // We shouldn't be receiving any further messages on this exchange.
+ //
+ return CHIP_ERROR_INCORRECT_STATE;
+ }
+
+ void OnResponseTimeout(Messaging::ExchangeContext * ec) override
+ {
+ //
+ // We're not expecting responses to any messages we send out on this EC.
+ //
+ VerifyOrDie(false);
+ }
+
enum class State
{
Idle, ///< Default state that the object starts out in, where no work has commenced
@@ -343,14 +357,16 @@
return FinishCommand(/* aEndDataStruct = */ false);
}
- Messaging::ExchangeContext * mpExchangeCtx = nullptr;
- Callback * mpCallback = nullptr;
+ Messaging::ExchangeHolder mExchangeCtx;
+ Callback * mpCallback = nullptr;
InvokeResponseMessage::Builder mInvokeResponseBuilder;
TLV::TLVType mDataElementContainerType = TLV::kTLVType_NotSpecified;
size_t mPendingWork = 0;
bool mSuppressResponse = false;
bool mTimedRequest = false;
+ bool mSentStatusResponse = false;
+
State mState = State::Idle;
chip::System::PacketBufferTLVWriter mCommandMessageWriter;
TLV::TLVWriter mBackupWriter;
diff --git a/src/app/CommandSender.cpp b/src/app/CommandSender.cpp
index 6305375..803204d 100644
--- a/src/app/CommandSender.cpp
+++ b/src/app/CommandSender.cpp
@@ -33,7 +33,8 @@
namespace app {
CommandSender::CommandSender(Callback * apCallback, Messaging::ExchangeManager * apExchangeMgr, bool aIsTimedRequest) :
- mpCallback(apCallback), mpExchangeMgr(apExchangeMgr), mSuppressResponse(false), mTimedRequest(aIsTimedRequest)
+ mExchangeCtx(*this), mpCallback(apCallback), mpExchangeMgr(apExchangeMgr), mSuppressResponse(false),
+ mTimedRequest(aIsTimedRequest)
{}
CHIP_ERROR CommandSender::AllocateBuffer()
@@ -67,15 +68,17 @@
ReturnErrorOnFailure(Finalize(mPendingInvokeData));
// Create a new exchange context.
- mpExchangeCtx = mpExchangeMgr->NewContext(session, this);
- VerifyOrReturnError(mpExchangeCtx != nullptr, CHIP_ERROR_NO_MEMORY);
- VerifyOrReturnError(!mpExchangeCtx->IsGroupExchangeContext(), CHIP_ERROR_INVALID_MESSAGE_TYPE);
+ auto exchange = mpExchangeMgr->NewContext(session, this);
+ VerifyOrReturnError(exchange != nullptr, CHIP_ERROR_NO_MEMORY);
- mpExchangeCtx->SetResponseTimeout(timeout.ValueOr(session->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime)));
+ mExchangeCtx.Grab(exchange);
+ VerifyOrReturnError(!mExchangeCtx->IsGroupExchangeContext(), CHIP_ERROR_INVALID_MESSAGE_TYPE);
+
+ mExchangeCtx->SetResponseTimeout(timeout.ValueOr(session->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime)));
if (mTimedInvokeTimeoutMs.HasValue())
{
- ReturnErrorOnFailure(TimedRequest::Send(mpExchangeCtx, mTimedInvokeTimeoutMs.Value()));
+ ReturnErrorOnFailure(TimedRequest::Send(mExchangeCtx.Get(), mTimedInvokeTimeoutMs.Value()));
MoveToState(State::AwaitingTimedStatus);
return CHIP_NO_ERROR;
}
@@ -90,15 +93,14 @@
ReturnErrorOnFailure(Finalize(mPendingInvokeData));
// Create a new exchange context.
- mpExchangeCtx = mpExchangeMgr->NewContext(session, this);
- VerifyOrReturnError(mpExchangeCtx != nullptr, CHIP_ERROR_NO_MEMORY);
- VerifyOrReturnError(mpExchangeCtx->IsGroupExchangeContext(), CHIP_ERROR_INVALID_MESSAGE_TYPE);
+ auto exchange = mpExchangeMgr->NewContext(session, this);
+ VerifyOrReturnError(exchange != nullptr, CHIP_ERROR_NO_MEMORY);
+
+ mExchangeCtx.Grab(exchange);
+ VerifyOrReturnError(mExchangeCtx->IsGroupExchangeContext(), CHIP_ERROR_INVALID_MESSAGE_TYPE);
ReturnErrorOnFailure(SendInvokeRequest());
- // Exchange is gone now, since it closed itself on successful send.
- mpExchangeCtx = nullptr;
-
Close();
return CHIP_NO_ERROR;
}
@@ -108,8 +110,8 @@
using namespace Protocols::InteractionModel;
using namespace Messaging;
- ReturnErrorOnFailure(mpExchangeCtx->SendMessage(MsgType::InvokeCommandRequest, std::move(mPendingInvokeData),
- SendMessageFlags::kExpectResponse));
+ ReturnErrorOnFailure(
+ mExchangeCtx->SendMessage(MsgType::InvokeCommandRequest, std::move(mPendingInvokeData), SendMessageFlags::kExpectResponse));
MoveToState(State::CommandSent);
return CHIP_NO_ERROR;
@@ -124,7 +126,7 @@
}
CHIP_ERROR err = CHIP_NO_ERROR;
- VerifyOrExit(apExchangeContext == mpExchangeCtx, err = CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrExit(apExchangeContext == mExchangeCtx.Get(), err = CHIP_ERROR_INCORRECT_STATE);
if (mState == State::AwaitingTimedStatus)
{
@@ -223,18 +225,6 @@
mTimedRequest = false;
MoveToState(State::AwaitingDestruction);
- // OnDone below can destroy us before we unwind all the way back into the
- // exchange code and it tries to close itself. Make sure that it doesn't
- // try to notify us that it's closing, since we will be dead.
- //
- // For more details, see #10344.
- if (mpExchangeCtx != nullptr)
- {
- mpExchangeCtx->SetDelegate(nullptr);
- }
-
- mpExchangeCtx = nullptr;
-
if (mpCallback)
{
mpCallback->OnDone(this);
@@ -443,27 +433,5 @@
ChipLogDetail(DataManagement, "ICR moving to [%10.10s]", GetStateStr());
}
-void CommandSender::Abort()
-{
- //
- // If the exchange context hasn't already been gracefully closed
- // (signaled by setting it to null), then we need to forcibly
- // tear it down.
- //
- if (mpExchangeCtx != nullptr)
- {
- // We might be a delegate for this exchange, and we don't want the
- // OnExchangeClosing notification in that case. Null out the delegate
- // to avoid that.
- //
- // TODO: This makes all sorts of assumptions about what the delegate is
- // (notice the "might" above!) that might not hold in practice. We
- // really need a better solution here....
- mpExchangeCtx->SetDelegate(nullptr);
- mpExchangeCtx->Abort();
- mpExchangeCtx = nullptr;
- }
-}
-
} // namespace app
} // namespace chip
diff --git a/src/app/CommandSender.h b/src/app/CommandSender.h
index f0f0f94..e0ad257 100644
--- a/src/app/CommandSender.h
+++ b/src/app/CommandSender.h
@@ -38,7 +38,7 @@
#include <lib/support/CodeUtils.h>
#include <lib/support/DLLUtil.h>
#include <lib/support/logging/CHIPLogging.h>
-#include <messaging/ExchangeContext.h>
+#include <messaging/ExchangeHolder.h>
#include <messaging/ExchangeMgr.h>
#include <messaging/Flags.h>
#include <protocols/Protocols.h>
@@ -53,23 +53,6 @@
class CommandSender final : public Messaging::ExchangeDelegate
{
public:
- /*
- * Destructor - as part of destruction, it will abort the exchange context
- * if a valid one still exists.
- *
- * See Abort() for details on when that might occur.
- */
- ~CommandSender() override { Abort(); }
-
- /**
- * Gets the inner exchange context object, without ownership.
- *
- * @return The inner exchange context, might be nullptr if no
- * exchange context has been assigned or the context
- * has been released.
- */
- Messaging::ExchangeContext * GetExchangeContext() const { return mpExchangeCtx; }
-
class Callback
{
public:
@@ -290,7 +273,7 @@
CHIP_ERROR Finalize(System::PacketBufferHandle & commandPacket);
- Messaging::ExchangeContext * mpExchangeCtx = nullptr;
+ Messaging::ExchangeHolder mExchangeCtx;
Callback * mpCallback = nullptr;
Messaging::ExchangeManager * mpExchangeMgr = nullptr;
InvokeRequestMessage::Builder mInvokeRequestBuilder;
diff --git a/src/app/WriteClient.cpp b/src/app/WriteClient.cpp
index c17c63a..3e70a00 100644
--- a/src/app/WriteClient.cpp
+++ b/src/app/WriteClient.cpp
@@ -35,46 +35,12 @@
{
MoveToState(State::AwaitingDestruction);
- // OnDone below can destroy us before we unwind all the way back into the
- // exchange code and it tries to close itself. Make sure that it doesn't
- // try to notify us that it's closing, since we will be dead.
- //
- // For more details, see #10344.
- if (mpExchangeCtx != nullptr)
- {
- mpExchangeCtx->SetDelegate(nullptr);
- }
-
- mpExchangeCtx = nullptr;
-
if (mpCallback)
{
mpCallback->OnDone(this);
}
}
-void WriteClient::Abort()
-{
- //
- // If the exchange context hasn't already been gracefully closed
- // (signaled by setting it to null), then we need to forcibly
- // tear it down.
- //
- if (mpExchangeCtx != nullptr)
- {
- // We might be a delegate for this exchange, and we don't want the
- // OnExchangeClosing notification in that case. Null out the delegate
- // to avoid that.
- //
- // TODO: This makes all sorts of assumptions about what the delegate is
- // (notice the "might" above!) that might not hold in practice. We
- // really need a better solution here....
- mpExchangeCtx->SetDelegate(nullptr);
- mpExchangeCtx->Abort();
- mpExchangeCtx = nullptr;
- }
-}
-
CHIP_ERROR WriteClient::ProcessWriteResponseMessage(System::PacketBufferHandle && payload)
{
CHIP_ERROR err = CHIP_NO_ERROR;
@@ -365,23 +331,28 @@
err = FinalizeMessage(false /* hasMoreChunks */);
SuccessOrExit(err);
- // Create a new exchange context.
- mpExchangeCtx = mpExchangeMgr->NewContext(session, this);
- VerifyOrExit(mpExchangeCtx != nullptr, err = CHIP_ERROR_NO_MEMORY);
- VerifyOrReturnError(!(mpExchangeCtx->IsGroupExchangeContext() && mHasDataVersion), CHIP_ERROR_INVALID_MESSAGE_TYPE);
+ {
+ // Create a new exchange context.
+ auto exchange = mpExchangeMgr->NewContext(session, this);
+ VerifyOrExit(exchange != nullptr, err = CHIP_ERROR_NO_MEMORY);
+
+ mExchangeCtx.Grab(exchange);
+ }
+
+ VerifyOrReturnError(!(mExchangeCtx->IsGroupExchangeContext() && mHasDataVersion), CHIP_ERROR_INVALID_MESSAGE_TYPE);
if (timeout == System::Clock::kZero)
{
- mpExchangeCtx->UseSuggestedResponseTimeout(app::kExpectedIMProcessingTime);
+ mExchangeCtx->UseSuggestedResponseTimeout(app::kExpectedIMProcessingTime);
}
else
{
- mpExchangeCtx->SetResponseTimeout(timeout);
+ mExchangeCtx->SetResponseTimeout(timeout);
}
if (mTimedWriteTimeoutMs.HasValue())
{
- err = TimedRequest::Send(mpExchangeCtx, mTimedWriteTimeoutMs.Value());
+ err = TimedRequest::Send(mExchangeCtx.Get(), mTimedWriteTimeoutMs.Value());
SuccessOrExit(err);
MoveToState(State::AwaitingTimedStatus);
}
@@ -425,7 +396,7 @@
System::PacketBufferHandle data = mChunks.PopHead();
- bool isGroupWrite = mpExchangeCtx->IsGroupExchangeContext();
+ bool isGroupWrite = mExchangeCtx->IsGroupExchangeContext();
if (!mChunks.IsNull() && isGroupWrite)
{
// Reject this request if we have more than one chunk (mChunks is not null after PopHead()), and this is a group
@@ -434,13 +405,8 @@
}
// kExpectResponse is ignored by ExchangeContext in case of groupcast
- ReturnErrorOnFailure(mpExchangeCtx->SendMessage(MsgType::WriteRequest, std::move(data), SendMessageFlags::kExpectResponse));
- if (isGroupWrite)
- {
- // Exchange is closed now, since there are no group responses. Drop our
- // ref to it.
- mpExchangeCtx = nullptr;
- }
+ ReturnErrorOnFailure(mExchangeCtx->SendMessage(MsgType::WriteRequest, std::move(data), SendMessageFlags::kExpectResponse));
+
MoveToState(State::AwaitingResponse);
return CHIP_NO_ERROR;
}
@@ -456,11 +422,12 @@
}
CHIP_ERROR err = CHIP_NO_ERROR;
+
// Assert that the exchange context matches the client's current context.
// This should never fail because even if SendWriteRequest is called
// back-to-back, the second call will call Close() on the first exchange,
// which clears the OnMessageReceived callback.
- VerifyOrExit(apExchangeContext == mpExchangeCtx, err = CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrExit(apExchangeContext == mExchangeCtx.Get(), err = CHIP_ERROR_INCORRECT_STATE);
if (mState == State::AwaitingTimedStatus)
{
diff --git a/src/app/WriteClient.h b/src/app/WriteClient.h
index 2f7ca23..395809d 100644
--- a/src/app/WriteClient.h
+++ b/src/app/WriteClient.h
@@ -33,7 +33,7 @@
#include <lib/support/CodeUtils.h>
#include <lib/support/DLLUtil.h>
#include <lib/support/logging/CHIPLogging.h>
-#include <messaging/ExchangeContext.h>
+#include <messaging/ExchangeHolder.h>
#include <messaging/ExchangeMgr.h>
#include <messaging/Flags.h>
#include <protocols/Protocols.h>
@@ -125,14 +125,15 @@
WriteClient(Messaging::ExchangeManager * apExchangeMgr, Callback * apCallback, const Optional<uint16_t> & aTimedWriteTimeoutMs,
bool aSuppressResponse = false) :
mpExchangeMgr(apExchangeMgr),
- mpCallback(apCallback), mTimedWriteTimeoutMs(aTimedWriteTimeoutMs), mSuppressResponse(aSuppressResponse)
+ mExchangeCtx(*this), mpCallback(apCallback), mTimedWriteTimeoutMs(aTimedWriteTimeoutMs),
+ mSuppressResponse(aSuppressResponse)
{}
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
WriteClient(Messaging::ExchangeManager * apExchangeMgr, Callback * apCallback, const Optional<uint16_t> & aTimedWriteTimeoutMs,
uint16_t aReservedSize) :
mpExchangeMgr(apExchangeMgr),
- mpCallback(apCallback), mTimedWriteTimeoutMs(aTimedWriteTimeoutMs), mReservedSize(aReservedSize)
+ mExchangeCtx(*this), mpCallback(apCallback), mTimedWriteTimeoutMs(aTimedWriteTimeoutMs), mReservedSize(aReservedSize)
{}
#endif
@@ -226,14 +227,6 @@
*/
void Shutdown();
- /*
- * Destructor - as part of destruction, it will abort the exchange context
- * if a valid one still exists.
- *
- * See Abort() for details on when that might occur.
- */
- ~WriteClient() override { Abort(); }
-
private:
friend class TestWriteInteraction;
friend class InteractionModelEngine;
@@ -378,9 +371,9 @@
CHIP_ERROR FinalizeMessage(bool aHasMoreChunks);
Messaging::ExchangeManager * mpExchangeMgr = nullptr;
- Messaging::ExchangeContext * mpExchangeCtx = nullptr;
- Callback * mpCallback = nullptr;
- State mState = State::Initialized;
+ Messaging::ExchangeHolder mExchangeCtx;
+ Callback * mpCallback = nullptr;
+ State mState = State::Initialized;
System::PacketBufferTLVWriter mMessageWriter;
WriteRequestMessage::Builder mWriteRequestBuilder;
// TODO Maybe we should change PacketBufferTLVWriter so we can finalize it
diff --git a/src/app/WriteHandler.cpp b/src/app/WriteHandler.cpp
index ba52461..3ede722 100644
--- a/src/app/WriteHandler.cpp
+++ b/src/app/WriteHandler.cpp
@@ -36,7 +36,7 @@
CHIP_ERROR WriteHandler::Init()
{
- VerifyOrReturnError(mpExchangeCtx == nullptr, CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrReturnError(!mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);
MoveToState(State::Initialized);
@@ -51,31 +51,11 @@
mSuppressResponse = false;
VerifyOrReturn(mState != State::Uninitialized);
- if (mpExchangeCtx != nullptr)
- {
- mpExchangeCtx->SetDelegate(nullptr);
- mpExchangeCtx = nullptr;
- }
-
ClearState();
}
void WriteHandler::Abort()
{
- if (mpExchangeCtx != nullptr)
- {
- // We might be a delegate for this exchange, and we don't want the
- // OnExchangeClosing notification in that case. Null out the delegate
- // to avoid that.
- //
- // TODO: This makes all sorts of assumptions about what the delegate is
- // (notice the "might" above!) that might not hold in practice. We
- // really need a better solution here....
- mpExchangeCtx->SetDelegate(nullptr);
- mpExchangeCtx->Abort();
- mpExchangeCtx = nullptr;
- }
-
ClearState();
}
@@ -110,13 +90,11 @@
Status WriteHandler::OnWriteRequest(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload,
bool aIsTimedWrite)
{
- mpExchangeCtx = apExchangeContext;
-
//
// Let's take over further message processing on this exchange from the IM.
// This is only relevant during chunked requests.
//
- mpExchangeCtx->SetDelegate(this);
+ mExchangeCtx.Grab(apExchangeContext);
Status status = HandleWriteRequestMessage(apExchangeContext, std::move(aPayload), aIsTimedWrite);
@@ -134,7 +112,7 @@
{
CHIP_ERROR err = CHIP_NO_ERROR;
- VerifyOrDieWithMsg(apExchangeContext == mpExchangeCtx, DataManagement,
+ VerifyOrDieWithMsg(apExchangeContext == mExchangeCtx.Get(), DataManagement,
"Incoming exchange context should be same as the initial request.");
VerifyOrDieWithMsg(!apExchangeContext->IsGroupExchangeContext(), DataManagement,
"OnMessageReceived should not be called on GroupExchangeContext");
@@ -191,11 +169,11 @@
err = FinalizeMessage(std::move(aMessageWriter), packet);
SuccessOrExit(err);
- VerifyOrExit(mpExchangeCtx != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
- mpExchangeCtx->UseSuggestedResponseTimeout(app::kExpectedIMProcessingTime);
- err = mpExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::WriteResponse, std::move(packet),
- mHasMoreChunks ? Messaging::SendMessageFlags::kExpectResponse
- : Messaging::SendMessageFlags::kNone);
+ VerifyOrExit(mExchangeCtx, err = CHIP_ERROR_INCORRECT_STATE);
+ mExchangeCtx->UseSuggestedResponseTimeout(app::kExpectedIMProcessingTime);
+ err = mExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::WriteResponse, std::move(packet),
+ mHasMoreChunks ? Messaging::SendMessageFlags::kExpectResponse
+ : Messaging::SendMessageFlags::kNone);
SuccessOrExit(err);
MoveToState(State::Sending);
@@ -237,7 +215,7 @@
Credentials::GroupDataProvider * groupDataProvider = Credentials::GetGroupDataProvider();
Credentials::GroupDataProvider::EndpointIterator * iterator;
- GroupId groupId = mpExchangeCtx->GetSessionHandle()->AsIncomingGroupSession()->GetGroupId();
+ GroupId groupId = mExchangeCtx->GetSessionHandle()->AsIncomingGroupSession()->GetGroupId();
FabricIndex fabricIndex = GetAccessingFabricIndex();
auto processingConcreteAttributePath = mProcessingAttributePath.Value();
@@ -289,8 +267,8 @@
{
CHIP_ERROR err = CHIP_NO_ERROR;
- ReturnErrorCodeIf(mpExchangeCtx == nullptr, CHIP_ERROR_INTERNAL);
- const Access::SubjectDescriptor subjectDescriptor = mpExchangeCtx->GetSessionHandle()->GetSubjectDescriptor();
+ ReturnErrorCodeIf(!mExchangeCtx, CHIP_ERROR_INTERNAL);
+ const Access::SubjectDescriptor subjectDescriptor = mExchangeCtx->GetSessionHandle()->GetSubjectDescriptor();
while (CHIP_NO_ERROR == (err = aAttributeDataIBsReader.Next()))
{
@@ -396,11 +374,11 @@
{
CHIP_ERROR err = CHIP_NO_ERROR;
- ReturnErrorCodeIf(mpExchangeCtx == nullptr, CHIP_ERROR_INTERNAL);
+ ReturnErrorCodeIf(!mExchangeCtx, CHIP_ERROR_INTERNAL);
const Access::SubjectDescriptor subjectDescriptor =
- mpExchangeCtx->GetSessionHandle()->AsIncomingGroupSession()->GetSubjectDescriptor();
+ mExchangeCtx->GetSessionHandle()->AsIncomingGroupSession()->GetSubjectDescriptor();
- GroupId groupId = mpExchangeCtx->GetSessionHandle()->AsIncomingGroupSession()->GetGroupId();
+ GroupId groupId = mExchangeCtx->GetSessionHandle()->AsIncomingGroupSession()->GetGroupId();
FabricIndex fabric = GetAccessingFabricIndex();
while (CHIP_NO_ERROR == (err = aAttributeDataIBsReader.Next()))
@@ -590,7 +568,7 @@
}
SuccessOrExit(err);
- if (mHasMoreChunks && (mpExchangeCtx->IsGroupExchangeContext() || mIsTimedRequest))
+ if (mHasMoreChunks && (mExchangeCtx->IsGroupExchangeContext() || mIsTimedRequest))
{
// Sanity check: group exchange context should only have one chunk.
// Also, timed requests should not have more than one chunk.
@@ -610,7 +588,7 @@
AttributeDataIBsParser.GetReader(&AttributeDataIBsReader);
- if (mpExchangeCtx->IsGroupExchangeContext())
+ if (mExchangeCtx->IsGroupExchangeContext())
{
err = ProcessGroupAttributeDataIBs(AttributeDataIBsReader);
}
@@ -680,7 +658,7 @@
FabricIndex WriteHandler::GetAccessingFabricIndex() const
{
- return mpExchangeCtx->GetSessionHandle()->GetFabricIndex();
+ return mExchangeCtx->GetSessionHandle()->GetFabricIndex();
}
const char * WriteHandler::GetStateStr() const
@@ -712,6 +690,7 @@
void WriteHandler::ClearState()
{
DeliverFinalListWriteEnd(false /* wasSuccessful */);
+ mExchangeCtx.Release();
MoveToState(State::Uninitialized);
}
diff --git a/src/app/WriteHandler.h b/src/app/WriteHandler.h
index 1edf40b..43163a2 100644
--- a/src/app/WriteHandler.h
+++ b/src/app/WriteHandler.h
@@ -25,7 +25,7 @@
#include <lib/support/CodeUtils.h>
#include <lib/support/DLLUtil.h>
#include <lib/support/logging/CHIPLogging.h>
-#include <messaging/ExchangeContext.h>
+#include <messaging/ExchangeHolder.h>
#include <messaging/ExchangeMgr.h>
#include <messaging/Flags.h>
#include <protocols/Protocols.h>
@@ -41,6 +41,8 @@
class WriteHandler : public Messaging::ExchangeDelegate
{
public:
+ WriteHandler() : mExchangeCtx(*this) {}
+
/**
* Initialize the WriteHandler. Within the lifetime
* of this instance, this method is invoked once after object
@@ -96,7 +98,7 @@
bool MatchesExchangeContext(Messaging::ExchangeContext * apExchangeContext) const
{
- return !IsFree() && mpExchangeCtx == apExchangeContext;
+ return !IsFree() && mExchangeCtx.Get() == apExchangeContext;
}
void CacheACLCheckResult(const AttributeAccessToken & aToken) { mACLCheckCache.SetValue(aToken); }
@@ -158,7 +160,7 @@
System::PacketBufferHandle && aPayload) override;
void OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) override;
- Messaging::ExchangeContext * mpExchangeCtx = nullptr;
+ Messaging::ExchangeHolder mExchangeCtx;
WriteResponseMessage::Builder mWriteResponseBuilder;
State mState = State::Uninitialized;
bool mIsTimedRequest = false;
diff --git a/src/app/tests/TestCommandInteraction.cpp b/src/app/tests/TestCommandInteraction.cpp
index 6b7a089..06a007c 100644
--- a/src/app/tests/TestCommandInteraction.cpp
+++ b/src/app/tests/TestCommandInteraction.cpp
@@ -452,8 +452,8 @@
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
TestExchangeDelegate delegate;
- commandHandler.mpExchangeCtx = ctx.NewExchangeToAlice(&delegate);
- err = commandHandler.SendCommandResponse();
+ commandHandler.mExchangeCtx.Grab(ctx.NewExchangeToAlice(&delegate));
+ err = commandHandler.SendCommandResponse();
NL_TEST_ASSERT(apSuite, err == CHIP_ERROR_INCORRECT_STATE);
}
@@ -488,7 +488,7 @@
System::PacketBufferHandle commandDatabuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
TestExchangeDelegate delegate;
- commandHandler.mpExchangeCtx = ctx.NewExchangeToAlice(&delegate);
+ commandHandler.mExchangeCtx.Grab(ctx.NewExchangeToAlice(&delegate));
err = commandHandler.PrepareCommand(path);
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
@@ -521,7 +521,7 @@
System::PacketBufferHandle commandPacket;
TestExchangeDelegate delegate;
- commandHandler.mpExchangeCtx = ctx.NewExchangeToAlice(&delegate);
+ commandHandler.mExchangeCtx.Grab(ctx.NewExchangeToAlice(&delegate));
AddInvokeResponseData(apSuite, apContext, &commandHandler, aNeedStatusCode);
err = commandHandler.Finalize(commandPacket);
@@ -581,7 +581,7 @@
System::PacketBufferHandle commandPacket;
TestExchangeDelegate delegate;
- commandHandler.mpExchangeCtx = ctx.NewExchangeToAlice(&delegate);
+ commandHandler.mExchangeCtx.Grab(ctx.NewExchangeToAlice(&delegate));
auto path = MakeTestCommandPath();
@@ -608,7 +608,7 @@
System::PacketBufferHandle commandPacket;
TestExchangeDelegate delegate;
- commandHandler.mpExchangeCtx = ctx.NewExchangeToAlice(&delegate);
+ commandHandler.mExchangeCtx.Grab(ctx.NewExchangeToAlice(&delegate));
auto path = MakeTestCommandPath();
@@ -635,7 +635,7 @@
System::PacketBufferHandle commandPacket;
TestExchangeDelegate delegate;
- commandHandler.mpExchangeCtx = ctx.NewExchangeToAlice(&delegate);
+ commandHandler.mExchangeCtx.Grab(ctx.NewExchangeToAlice(&delegate));
auto path = MakeTestCommandPath();
@@ -672,7 +672,7 @@
System::PacketBufferHandle commandDatabuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
TestExchangeDelegate delegate;
- commandHandler.mpExchangeCtx = ctx.NewExchangeToAlice(&delegate);
+ commandHandler.mExchangeCtx.Grab(ctx.NewExchangeToAlice(&delegate));
GenerateInvokeRequest(apSuite, apContext, commandDatabuf, /* aIsTimedRequest = */ false, kTestCommandIdWithData);
err = commandHandler.ProcessInvokeRequest(std::move(commandDatabuf), false);
@@ -711,7 +711,7 @@
System::PacketBufferHandle commandDatabuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
TestExchangeDelegate delegate;
- commandHandler.mpExchangeCtx = ctx.NewExchangeToAlice(&delegate);
+ commandHandler.mExchangeCtx.Grab(ctx.NewExchangeToAlice(&delegate));
chip::isCommandDispatched = false;
GenerateInvokeRequest(apSuite, apContext, commandDatabuf, messageIsTimed, kTestCommandIdNoData);
diff --git a/src/app/tests/TestWriteInteraction.cpp b/src/app/tests/TestWriteInteraction.cpp
index 9836c89..f1af80c 100644
--- a/src/app/tests/TestWriteInteraction.cpp
+++ b/src/app/tests/TestWriteInteraction.cpp
@@ -306,15 +306,6 @@
}
else
{
- //
- // In a normal execution flow, the exchange manager would have closed out the exchange after the
- // message dispatch call path had unwound. In this test however, we've manually allocated the exchange
- // ourselves (as opposed to the exchange manager), so we need to take ownership of closing out the exchange.
- //
- // Note that this doesn't happen in the success case above, since that results in a call to send a message through
- // the exchange context, which results in the exchange manager correctly closing it.
- //
- exchange->Close();
NL_TEST_ASSERT(apSuite, status == Status::UnsupportedAccess);
}
diff --git a/src/messaging/ExchangeHolder.h b/src/messaging/ExchangeHolder.h
index 7bb2745..fb7e2cd 100644
--- a/src/messaging/ExchangeHolder.h
+++ b/src/messaging/ExchangeHolder.h
@@ -64,6 +64,8 @@
*/
void Grab(ExchangeContext * exchange)
{
+ VerifyOrDie(exchange != nullptr);
+
Release();
mpExchangeCtx = exchange;