// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//
/**
@file
*/
#include "mm_activities.h"
#include <elements/mm_context.h>
#include <elements/mm_states.h>
#include <elements/nm_messages_base.h>
#include <elements/nm_messages_child.h>
#include <elements/nm_messages_errorrecovery.h>
#ifdef _DEBUG
// Panic category for "absolutely impossible!" vanilla ASSERT()-type panics from this module
// (if it could happen through user error then you should give it an explicit, documented, category + code)
_LIT(KSpecAssert_ElemMeshMachActC, "ElemMeshMachActC");
#endif
using namespace MeshMachine;
using namespace Messages;
using namespace Elements;
using namespace NetStateMachine;
//-=========================================================
//
//Panics
//
//-=========================================================
_LIT (KMMActivityPanic,"MMActivityPanic");
enum
{
EPanicCorruptedContext = 1,
EPanicNoPreallocatedSpace = 2
};
//-=========================================================
//
//TNodeActivityIter
//
//-=========================================================
const TNodeActivity* TNodeActivityIter::FetchActivity()
{
const TNodeActivity* a = NULL;
if (*iCurrentEntry)
{
a = &(*iCurrentEntry)();
++iCurrentEntry;
}
else
{
if (NULL!=*(iCurrentEntry+1))
{
const TNodeActivityMap::TStaticNodeActivityMap& nextMapFn = (const TNodeActivityMap::TStaticNodeActivityMap&)*(iCurrentEntry+1);
iCurrentEntry = &nextMapFn().iFirstActivity;
a = FetchActivity();
}
}
return a;
}
//-=========================================================
//
//CNodeActivityBase
//
//-=========================================================
EXPORT_C CNodeActivityBase* CNodeActivityBase::NewL(const TNodeActivity& aActivitySig, AMMNodeBase& aNode)
{
return new(ELeave)CNodeActivityBase(aActivitySig, aNode);
}
EXPORT_C CNodeActivityBase::CNodeActivityBase(const TNodeActivity& aActivitySig, AMMNodeBase& aNode)
: iNode(aNode),
iActivitySig(aActivitySig)
{
}
EXPORT_C CNodeActivityBase::~CNodeActivityBase()
{
if(iError != KErrNone)
{
PostToOriginators(TEBase::TError(KickOffMessageId(), iError).CRef());
}
//If an activity constructed custom originator's interfaces, now it needs to clean them up
for (TInt i = iOriginators.Count() - 1; i >= 0; --i)
{
RemoveOriginator(i);
}
iOriginators.Close();
}
EXPORT_C NetInterfaces::TInterfaceControl* CNodeActivityBase::DoFetchInterfaceControlL(TInt /*aInterfaceId*/)
/** Allows to fetch an arbitrary interface instance from the client
@internalTechnology
*/
{
return this; //as a base we assume are the control for any interface any derivative may implement
}
//static
const TStateTriple* CNodeActivityBase::Accept(TNodeContextBase& aContext, const TNodeActivity& aActivitySig, TInt aTransitionTag)
{
const TStateTriple& first = aActivitySig.iFirstTriple;
//in debug only(!) check if we are in the first triple as expected
__ASSERT_DEBUG((&first-1)->iSCtor==NULL && (&first-1)->iTCtor==NULL, User::Panic(KSpecAssert_ElemMeshMachActC, 1));
TBool accept = (aActivitySig.iKickOffMessageId == KNullMessageId
|| (aActivitySig.iKickOffMessageId == aContext.iMessage.MessageId().MessageId() &&
aActivitySig.iKickOffMessageRealm == aContext.iMessage.MessageId().Realm())
)
&& NetStateMachine::ACore::Accept(first, &aContext, aTransitionTag);
//if the activity accepted the message and the first triple has a transition, we execute it here
//NOTE that the first transition is called BEFORE the originator (sender of the message)
//is added to the activity list
if (accept && first.iTCtor)
{
TBuf8<KMaxStateClassByteSize> mem;
ACore::Do(first,&aContext,mem);
MESH_LOG((KMeshMachineSubTag, _L8("CNodeActivityBase:\tAccept->first transition [ANode=0x%08x] [Activity=%s] [Triple=%s]"),
&aContext.NodeId().Node(), aActivitySig.iName ? aActivitySig.iName : _S8("Undefined"), first.iName? first.iName : _S8("Undefined")));
}
if (accept && aContext.iReturn == KErrNone)
{
// MESH_LOG((KMeshMachineSubTag, _L8("CNodeActivityBase:\tAccept->accepted")));
return &first;
}
return NULL;
}
EXPORT_C void CNodeActivityBase::StartL(TNodeContextBase& aContext, const XNodePeerId& aOriginator, const TStateTriple& aFirst)
{
__ASSERT_DEBUG(FindOriginator(aOriginator) == KErrNotFound, User::Panic(KSpecAssert_ElemMeshMachActC, 2));
iOriginators.AppendL(aOriginator);
MESH_LOG_ACTIVITY_EXT(KMeshMachineSubTag, this, &aContext, (_L8("CNodeActivityBase %08x:\tStartL->starting activity"), this));
if (IsIdle())
{
NetStateMachine::ACore::Start(&aContext, aFirst);
MESH_LOG_ACTIVITY_EXT(KMeshMachineSubTag, this, &aContext, (_L8("CNodeActivityBase %08x:\tStartL->activity started"), this));
}
}
EXPORT_C TBool CNodeActivityBase::MatchSender(const TNodeContextBase& aContext) const
{
//The role of this method is to filter out all messages that 'this' should
//not be bothered with. 'this' should be interested in messages coming from
//what's set as iPostedToId or from one of the orginators.
//if the message's recipient specifies the activity id, then that
//activity must much that of 'this'.
TBool sender = iPostedToId.IsNull() ||
aContext.iSender == iPostedToId ||
FindOriginator(aContext.iSender) != KErrNotFound;
const TNodeCtxId* recipient = address_cast<const TNodeCtxId>(&aContext.iRecipient);
TBool activity = (recipient == NULL || ActivityId() == recipient->NodeCtx());
#ifdef SYMBIAN_TRACE_ENABLE
//If didn't match, trace why.
if (!(sender && activity))
{
NM_LOG_START_BLOCK(KMeshMachineSubTag, _L8("CNodeActivityBase::MatchSender"));
if(!sender)
{
MESH_LOG((KMeshMachineSubTag(), _L8("CNodeActivityBase %08x:\tiPostedToId mismatch:"), this));
NM_LOG_ADDRESS(KMeshMachineSubTag(), iPostedToId);
NM_LOG_ADDRESS(KMeshMachineSubTag(), aContext.iSender);
MESH_LOG((KMeshMachineSubTag(), _L8("CNodeActivityBase %08x:\toriginators' mismatch:"), this));
for (TInt i = iOriginators.Count() - 1; i>=0; i--)
{
NM_LOG_ADDRESS(KMeshMachineSubTag(), iOriginators[i].RecipientId());
}
}
if(!activity)
{
MESH_LOG((KMeshMachineSubTag(), _L8("CNodeActivityBase %08x:\tactivity mismatch [ours 0x%x, recipient 0x%x]"), this, ActivityId(), recipient->NodeCtx()));
}
NM_LOG_END_BLOCK(KMeshMachineSubTag(), KNullDesC8());
}
#endif
return sender && activity;
}
//static
EXPORT_C TAny* CNodeActivityBase::BorrowPreallocatedSpace(AMMNodeBase& aNode, TUint aSize)
{
return aNode.BorrowPreallocatedSpace(aSize);
}
EXPORT_C void CNodeActivityBase::ReturnPreallocatedSpace(TAny* aSpace)
{
return iNode.ReturnPreallocatedSpace(aSpace);
}
EXPORT_C void CNodeActivityBase::AppendActivityL()
{
//Based on the fact that there can ever be only one preallocated activity active at a time
//it is sufficient to always maintain one reserved space in the activity array.
//NOTE: ReserveL() will not cause table expansion when the already allocated space is
//big enough to hold the new count = the array will not grow indefinietly.
iNode.iActivities.ReserveL(iNode.iActivities.Count() + iNode.MaxPreallocatedActivityCount() + 1); //Reserve for additional + potential preallocated)
//Can not fail now
__ASSERT_ALWAYS(iNode.iActivities.Append(this) == KErrNone, User::Panic(KMMActivityPanic, EPanicNoPreallocatedSpace));
}
EXPORT_C void CNodeActivityBase::AppendPreallocatedActivity()
{
//Can not fail now
__ASSERT_ALWAYS(iNode.iActivities.Append(this) == KErrNone, User::Panic(KMMActivityPanic, EPanicNoPreallocatedSpace));
}
EXPORT_C void CNodeActivityBase::InsertPreallocatedDestroyActivity()
{
//Can not fail now
__ASSERT_ALWAYS(iNode.iActivities.Insert(this, 0) == KErrNone, User::Panic(KMMActivityPanic, EPanicNoPreallocatedSpace));
}
EXPORT_C TInt CNodeActivityBase::FindOriginator(const RNodeInterface& aPeerToFind) const
{
for (TInt i = iOriginators.Count() - 1; i>=0; i--)
{
//When you see a panic here this is probably because your originator
//stopped being a peer and this node failed to remove it from the activity originator's list.
if (iOriginators[i] == aPeerToFind.RecipientId())
{
return i;
}
}
return KErrNotFound;
}
EXPORT_C TInt CNodeActivityBase::FindOriginator(const TRuntimeCtxId& aPeerToFind) const
{
for (TInt i = iOriginators.Count() - 1; i>=0; i--)
{
//When you see a panic here this is probably because your originator
//stopped being a peer and this node failed to remove it from the activity originator's list.
if (iOriginators[i] == aPeerToFind)
{
return i;
}
}
return KErrNotFound;
}
EXPORT_C TInt CNodeActivityBase::FindOriginator(const TNodePeerId& aOriginator) const
{
for (TInt i = iOriginators.Count() - 1; i>=0; i--)
{
//When you see a panic here this is probably because your originator
//stopped being a peer and this node failed to remove it from the activity originator's list.
if (iOriginators[i] == aOriginator)
{
return i;
}
}
return KErrNotFound;
}
EXPORT_C TUint16 CNodeActivityBase::ActivityId() const
{
return ActivitySigId();
}
//You should use this function to remove originators to prevent memory leaking!
EXPORT_C void CNodeActivityBase::RemoveOriginator(TInt aIndex)
{
__ASSERT_DEBUG(aIndex>=0 && aIndex<iOriginators.Count(), User::Panic(KSpecAssert_ElemMeshMachActC, 3));
iOriginators[aIndex].Destroy();
iOriginators.Remove(aIndex);
}
EXPORT_C void CNodeActivityBase::SetIdle()
{
MESH_LOG((KMeshMachineSubTag, _L8("CNodeActivityBase %08x:\tSetIdle"), this));
NetStateMachine::ACore::SetIdle();
}
EXPORT_C TBool CNodeActivityBase::Next(TNodeContextBase& aContext)
{
TBool nextRet = EFalse;
//Check if this is the same id we have recently sent to
TBool senderMatch = MatchSender(aContext);
MESH_LOG_ACTIVITY_EXT(KMeshMachineSubTag, this, &aContext,
(senderMatch ? _L8("CNodeActivityBase %08x:\tNext->match") : _L8("CNodeActivityBase %08x:\tNext->NO match"), this));
if (senderMatch)
{
nextRet = ACore::Next(&aContext);
if(nextRet)
{
MESH_LOG_ACTIVITY_EXT(KMeshMachineSubTag, this, &aContext, (_L8("CNodeActivityBase %08x:\tNext->transition"), this));
}
}
return nextRet;
}
EXPORT_C void CNodeActivityBase::Cancel(TNodeContextBase& aContext)
{//we expect KErrCancel be set as a result of the state cancelation
MESH_LOG((KMeshMachineSubTag, _L8("CNodeActivityBase %08x:\tCancel(), iPostedToId %08x"), this, iPostedToId.Ptr() ? &iPostedToId.Node() : NULL));
if (!iPostedToId.IsNull())
{
RClientInterface::OpenPostMessageClose(TNodeCtxId(ActivityId(), iNode.Id()), iPostedToId, TEBase::TCancel().CRef());
}
else
{
NetStateMachine::ACore::Cancel(&aContext);
}
SetError(KErrCancel);
}
EXPORT_C TBool CNodeActivityBase::PostToOriginator(const TNodePeerId& aOriginator, const TSignalBase& aMessage) const
{
if (!(aOriginator.Flags() & TClientType::ELeaving))
{
aOriginator.PostMessage(TNodeCtxId(ActivityId(), iNode.Id()), aMessage);
}
else
{
MESH_LOG((KMeshMachineSubTag, _L8("CNodeActivityBase:\tPostToOriginator - IGNORING POST!")));
return EFalse;
}
return ETrue;
}
EXPORT_C TInt CNodeActivityBase::PostToOriginators(const TSignalBase& aMessage, TUint32 aFlagsToSet, TUint32 aFlagsToClear)
{
TInt msgSendCount=0;
for (TInt n = iOriginators.Count() - 1;n>=0; n--)
{
if (PostToOriginator(iOriginators[n], aMessage))
{
++msgSendCount;
iOriginators[n].Peer().SetFlags(aFlagsToSet);
iOriginators[n].Peer().ClearFlags(aFlagsToClear);
};
}
return msgSendCount;
}
EXPORT_C void CNodeActivityBase::SetPostedTo(const TNodeId& aNodeId)
{
iPostedToId = aNodeId;
}
EXPORT_C void CNodeActivityBase::ClearPostedTo()
{
iPostedToId.SetNull();
}
EXPORT_C void CNodeActivityBase::PostRequestTo(const RNodeInterface& aRecipient, const TSignalBase& aMessage, const TBool aRecipientIdCritical)
{
aRecipient.PostMessage(TNodeCtxId(ActivityId(), iNode.Id()), aMessage);
// Provide the option for the identity of the receipient to be unimportant when the response arrives
iPostedToId = aRecipientIdCritical ? aRecipient.RecipientId() : TNodeId::NullId();
}
//Avoid using this function, always prefer PostRequestTo(const RNodeInterface& aRecipient, const TNodeSignal& aMessage)
EXPORT_C void CNodeActivityBase::PostRequestTo(const TNodeId& aRecipient, const TSignalBase& aMessage, const TBool aRecipientIdCritical)
{
RClientInterface::OpenPostMessageClose(TNodeCtxId(ActivityId(), iNode.Id()), aRecipient, aMessage);
// Provide the option for the identity of the receipient to be unimportant when the response arrives
iPostedToId = aRecipientIdCritical ? aRecipient : TNodeId::NullId();
}
EXPORT_C TBool CNodeActivityBase::IsIdle() const
{
return NetStateMachine::ACore::IsIdle();
}
EXPORT_C void CNodeActivityBase::Abort(TNodeContextBase& aContext, TBool aIsNodeBeingDestroyed)
{
MESH_LOG((KMeshMachineSubTag, _L8("CNodeActivityBase %08x:\tAbort"), this));
//the error message will be send by message post processing see AMMNodeBase::Received
aContext.iReturn = KErrAbort; //before so as the error could be overwritten
NetStateMachine::ACore::Cancel(&aContext);
if (aContext.iReturn != KErrNone)
{
TEBase::TError error(aContext.iMessage.MessageId(), aContext.iReturn);
for (TInt n = iOriginators.Count() - 1;n>=0; n--)
{
TNodePeerId& originator = iOriginators[n];
//In the "quiet mode", when the hosting node is being destroyed, we can not afford sending
//an error to the node as it would hit void.
TBool canSend = !((aIsNodeBeingDestroyed && originator == aContext.NodeId())
|| aContext.iMessage.IsMessage<TEChild::TLeft>());
if (canSend)
{
PostToOriginator(originator, error);
}
RemoveOriginator(n); //Do not allow ~CNodeActivityBase to post as client's might be already gone
}
}
}
//-=========================================================
//
//CNodeRetryActivity
//
//-=========================================================
EXPORT_C CNodeRetryActivity::CNodeRetryActivity( const TNodeActivity& aActivitySig, AMMNodeBase& aNode )
: CNodeActivityBase(aActivitySig, aNode),
TIfStaticFetcherNearestInHierarchy(this)
{
}
EXPORT_C CNodeActivityBase* CNodeRetryActivity::NewL( const TNodeActivity& aActivitySig, AMMNodeBase& aNode )
{
return new(ELeave)CNodeRetryActivity(aActivitySig, aNode);
}
EXPORT_C TBool CNodeRetryActivity::IsIdle() const
{
return !IsWaiting() && CNodeActivityBase::IsIdle();
}
EXPORT_C void CNodeRetryActivity::SetIdle()
{
iContextDesc.Close();
ClearIsWaiting();
ClearWillWait(); //Set this too in case this fn was invoked from within the serialised state ot transition
CNodeActivityBase::SetIdle();
}
EXPORT_C TBool CNodeRetryActivity::Signal(TNodeContextBase& aContext)
{
return AActivitySemaphore::Signal(aContext);
}
EXPORT_C void CNodeRetryActivity::ReturnInterfacePtrL(AContextStore*& aInterface)
{
aInterface = this;
}
EXPORT_C void CNodeRetryActivity::ReturnInterfacePtrL(AActivitySemaphore*& aInterface)
{
aInterface = this;
}
//-=========================================================
//
//CNodeParallelActivityBase
//
//-=========================================================
// For custom activities to implement NewL
EXPORT_C TUint CNodeParallelActivityBase::GetNextActivityCountL( const TNodeActivity& aActivitySig, const AMMNodeBase& aNode )
{
TInt c = 1, i = 0;
const RPointerArray<CNodeActivityBase>& activities = aNode.Activities();
RArray<TInt> activityids;
CleanupClosePushL(activityids);
// collect the currently used ids
for (TInt i = 0; i < activities.Count(); i++)
{
TInt16 id = activities[i]->ActivityId();
if ((id&0xff) == aActivitySig.iId)
{
TInt8 uniqueid = id >> 8;
activityids.InsertInOrderL(uniqueid);
}
}
// find first available.
while (i < activityids.Count()
&& activityids[i] == c)
{
++i;
++c;
}
CleanupStack::PopAndDestroy(&activityids);
if(c > KActivityParallelRangeMax>>8)
{
User::Leave(KErrInUse);
}
return c;
}
EXPORT_C CNodeActivityBase* CNodeParallelActivityBase::NewL( const TNodeActivity& aActivitySig, AMMNodeBase& aNode )
{
TUint c = GetNextActivityCountL(aActivitySig,aNode);
return new(ELeave)CNodeParallelActivityBase(aActivitySig, aNode, c);
}
EXPORT_C CNodeParallelActivityBase::CNodeParallelActivityBase(const TNodeActivity& aActivitySig, AMMNodeBase& aNode, TUint aNextActivityCount)
: CNodeActivityBase(aActivitySig, aNode),
iActivityId((aNextActivityCount<<8)|aActivitySig.iId)
{
}
EXPORT_C TUint16 CNodeParallelActivityBase::ActivityId() const
{
return iActivityId;
}
//-=========================================================
//
//CNodeParallelMessageStoreActivityBase
//
//-=========================================================
EXPORT_C CNodeActivityBase* CNodeParallelMessageStoreActivityBase::NewL( const MeshMachine::TNodeActivity& aActivitySig, MeshMachine::AMMNodeBase& aNode )
{
TUint c = GetNextActivityCountL(aActivitySig,aNode);
return new (ELeave) CNodeParallelMessageStoreActivityBase( aActivitySig, aNode, c);
}
EXPORT_C CNodeParallelMessageStoreActivityBase::~CNodeParallelMessageStoreActivityBase()
{
}
EXPORT_C Messages::TSignalBase& CNodeParallelMessageStoreActivityBase::Message()
{
ASSERT(iMsg);
return *iMsg;
}
EXPORT_C void CNodeParallelMessageStoreActivityBase::StartL(TNodeContextBase& aContext, const Messages::XNodePeerId& aOriginator, const NetStateMachine::TStateTriple& aFirst)
{
SaveMessageL(aContext.iMessage);
CNodeParallelActivityBase::StartL(aContext, aOriginator, aFirst);
}
void CNodeParallelMessageStoreActivityBase::SaveMessageL(Messages::TSignalBase& aMessage)
{
Meta::CMetaDataVirtualCtorInPlace* vctr = TlsGlobals::Get().VirtualCtor();
TPtrC8 ptr(iMsgBuf);
iMsg = static_cast<TSignalBase*>(vctr->New(aMessage.GetTypeId(), iMsgBuf));
if (iMsg == NULL)
{
User::Leave(KErrNotFound);
}
iMsg->Copy(aMessage);
}
EXPORT_C CNodeParallelMessageStoreActivityBase::CNodeParallelMessageStoreActivityBase( const MeshMachine::TNodeActivity& aActivitySig, MeshMachine::AMMNodeBase& aNode, TUint aNextActivityCount )
: CNodeParallelActivityBase(aActivitySig, aNode, aNextActivityCount), iMsg(NULL)
{
}
//-=========================================================
//
//CNodeRetryParallelActivity
//
//-=========================================================
EXPORT_C CNodeRetryParallelActivity::CNodeRetryParallelActivity( const TNodeActivity& aActivitySig, AMMNodeBase& aNode, TUint aActivitiesCount )
: CNodeParallelActivityBase(aActivitySig, aNode, aActivitiesCount),
TIfStaticFetcherNearestInHierarchy(this)
{
}
EXPORT_C CNodeActivityBase* CNodeRetryParallelActivity::NewL( const TNodeActivity& aActivitySig, AMMNodeBase& aNode )
{
TUint c = GetNextActivityCountL(aActivitySig,aNode);
return new(ELeave)CNodeRetryParallelActivity(aActivitySig, aNode, c);
}
EXPORT_C TBool CNodeRetryParallelActivity::IsIdle() const
{
return !IsWaiting() && CNodeActivityBase::IsIdle();
}
EXPORT_C void CNodeRetryParallelActivity::SetIdle()
{
iContextDesc.Close();
ClearIsWaiting();
ClearWillWait(); //Set this too in case this fn was invoked from within the serialised state ot transition
CNodeActivityBase::SetIdle();
}
EXPORT_C TBool CNodeRetryParallelActivity::Signal(TNodeContextBase& aContext)
{
return AActivitySemaphore::Signal(aContext);
}
EXPORT_C void CNodeRetryParallelActivity::ReturnInterfacePtrL(AContextStore*& aInterface)
{
aInterface = this;
}
EXPORT_C void CNodeRetryParallelActivity::ReturnInterfacePtrL(AActivitySemaphore*& aInterface)
{
aInterface = this;
}
//-=========================================================
//
//AContextStore
//
//-=========================================================
EXPORT_C TInt AContextStore::StoreContext(const TNodeContextBase& aContext)
{//doesn't actually store TNodeContextBase only the message since it assumes
//that the transition context is the
//template <class TNODE, class TDerivedContext = TNodeContextBase>
//struct TNodeContext : public TDerivedContext
//where TDerivedContext = TNodeContextBase is always TNodeContextBase
//another type of context is not needed because all custom needs should be sorted by custom
//activity class
TInt error = KErrNone;
TInt len = TRuntimeCtxId::KMaxInlineAddressSize + aContext.iMessage.Length();
if (iContextDesc.MaxLength() < len)
{
iContextDesc.Close();
error = iContextDesc.Create(len);
}
else
{
iContextDesc.Zero();
}
if (KErrNone==error)
{
error = aContext.iSender.Store(iContextDesc); //would catch if iContextDesc has been allocated properly
}
if (KErrNone==error)
{
error = aContext.iMessage.Store(iContextDesc); //would catch if iContextDesc has been allocated properly
}
return error;
}
EXPORT_C TNodeContextBase* AContextStore::LoadContext(AMMNodeBase& aNode, CNodeActivityBase* aNodeActivity, TDes8& aCtxBuff, TDes8& aMsgBuff, const TNodeId& aDummy)
{
if (iContextDesc.Length() < sizeof(TNodeId))
{
return NULL;
}
const TRuntimeCtxId* sender = reinterpret_cast<const TRuntimeCtxId*>(iContextDesc.Ptr());
TPtrC8 contextStore(iContextDesc.Ptr()+sender->Size(),iContextDesc.Length()-sender->Size());
TSignatureBase* msg = static_cast<TSignatureBase*>(TlsGlobals::Get().VirtualCtor()->New(contextStore, aMsgBuff));
if (!msg)
{
return NULL;
}
//This assumes (as CNodeActivityBase::StoreContext) that the transition context is the
//template <class TNODE, class TDerivedContext = TNodeContextBase>
//struct TNodeContext : public TDerivedContext
//where TDerivedContext = TNodeContextBase is always TNodeContextBase.
//another type of context is not needed because all custom needs should be sorted by custom
//activity class
__ASSERT_DEBUG(aNodeActivity, User::Panic(KSpecAssert_ElemMeshMachActC, 4));
return ::new ((TUint8*)aCtxBuff.Ptr()) TNodeContextBase(aNode, *msg, *sender, aDummy, aNodeActivity);
}
EXPORT_C TBool AContextStore::IsStored() const
{
return iContextDesc.Length() >= sizeof(TNodeId);
}
// Needs to be moved to error-recoverable activity class
EXPORT_C const TNodeSignal::TMessageId& AContextStore::RetryingForMessageId() const
{
return iRetryingForMessageId;
}
//Called only ever from ReceivedL
EXPORT_C void AContextStore::Retry(CNodeActivityBase& aActivity, TNodeContextBase& aContext)
{
TBuf8<__Align8(sizeof(TNodeContextBase))> ctxBuf;
TBuf8<__Align8(TSignalBase::KMaxInlineMessageSize + TSignalBase::KMaxUnstoredOverhead)> msgBuf;
TNodeContextBase* storedContext = LoadContext(aContext.iNode, &aActivity,ctxBuf, msgBuf, aContext.NodeId());
//If we are here the context has been stored. There is no reason why it could not be loaded now.
//The only reason would be that the message factory was unregistered since the context was stored
//which is a critical condition.
__ASSERT_ALWAYS(storedContext, User::Panic(KMMActivityPanic,EPanicCorruptedContext));
TEErrorRecovery::TErrorRecoveryResponse& msg = message_cast<TEErrorRecovery::TErrorRecoveryResponse>(aContext.iMessage);
iRetryingForMessageId = msg.iErrResponse.iMessageId;
aActivity.ACore::DoCurrent(storedContext);
aContext.Node().HandleMessageReturnValue(*storedContext);
}
//-=========================================================
//
//AActivitySemaphore
//
//-=========================================================
EXPORT_C void AActivitySemaphore::ParkTransitionL(const TNodeContextBase& aContext)
{
__ASSERT_DEBUG(aContext.iNodeActivity, User::Panic(KSpecAssert_ElemMeshMachActC, 5));
AActivitySemaphore* as = static_cast<AActivitySemaphore*>(aContext.iNodeActivity->FetchExtInterfaceL(AActivitySemaphore::KInterfaceId));
if (!as->IsWaiting())
{
User::LeaveIfError(as->StoreContext(aContext));
//The context has been stored along with the message. Must consume the message, otherwise
//it will fall through and be seen by other activities or passthrough handling.
aContext.iMessage.ClearMessageId();
as->SetIsWaiting();
as->SetIsTransition();
MESH_LOG_CONTEXT_EXT(KMeshMachineSubTag, aContext, _L8("AActivitySemaphore:\tParkTransitionL->parked"));
}
as->SetWillWait();
__ASSERT_DEBUG(as->IsWaiting(), User::Panic(KSpecAssert_ElemMeshMachActC, 6)); //Must be stored and waiting now
}
EXPORT_C TInt AActivitySemaphore::ParkState(const TNodeContextBase& aContext)
{
__ASSERT_DEBUG(aContext.iNodeActivity, User::Panic(KSpecAssert_ElemMeshMachActC, 7));
AActivitySemaphore* as = static_cast<AActivitySemaphore*>(aContext.iNodeActivity->FetchExtInterface(AActivitySemaphore::KInterfaceId));
__ASSERT_DEBUG(as, User::Panic(KSpecAssert_ElemMeshMachActC, 8)); //State/transition mismatched with the activity object (activity doesn't support serialisation - wrong derivation?)
if (!as->IsWaiting())
{
TInt error = as->StoreContext(aContext);
if (error!=KErrNone)
{
return error;
};
//The context has been stored along with the message. Must consume the message, otherwise
//it will fall through and be seen by other activities or passthrough handling.
aContext.iMessage.ClearMessageId();
as->SetIsWaiting();
as->ClearIsTransition();
MESH_LOG_CONTEXT_EXT(KMeshMachineSubTag, aContext, _L8("AActivitySemaphore:\tParkState->parked"));
}
as->SetWillWait();
__ASSERT_DEBUG(as->IsWaiting(), User::Panic(KSpecAssert_ElemMeshMachActC, 9)); //Must be stored and waiting now
return KErrNone;
}
EXPORT_C TInt AActivitySemaphore::UnparkState(const TNodeContextBase& aContext)
{
__ASSERT_DEBUG(aContext.iNodeActivity, User::Panic(KSpecAssert_ElemMeshMachActC, 10));
AActivitySemaphore* as = static_cast<AActivitySemaphore*>(aContext.iNodeActivity->FetchExtInterface(AActivitySemaphore::KInterfaceId));
__ASSERT_DEBUG(as, User::Panic(KSpecAssert_ElemMeshMachActC, 11)); //State/transition mismatched with the activity object (activity doesn't support serialisation - wrong derivation?)
if (as->IsWaiting())
{
as->ClearIsWaiting();
MESH_LOG_CONTEXT_EXT(KMeshMachineSubTag, aContext, _L8("AActivitySemaphore:\tUnparkState->unparked"));
}
as->ClearWillWait();
__ASSERT_DEBUG(!as->IsWaiting(), User::Panic(KSpecAssert_ElemMeshMachActC, 12)); //Must be stored and waiting now
return KErrNone;
}
EXPORT_C void AActivitySemaphore::Wait()
{
MESH_LOG((KMeshMachineSubTag, _L8("AActivitySemaphore:\tWait->activity waiting")));
SetWillWait();
}
EXPORT_C TBool AActivitySemaphore::Signal(TNodeContextBase& aContext)
{
if (!(iFlags&KIsWaiting))
{
return EFalse;
}
//aContext.iNodeActivity is us
TBuf8<__Align8(sizeof(TNodeContextBase))> ctxBuf;
TBuf8<__Align8(TSignalBase::KMaxInlineMessageSize + TSignalBase::KMaxUnstoredOverhead)> msgBuf;
TNodeCtxId dummy(aContext.ActivityId(), aContext.NodeId());
TNodeContextBase* storedContext = LoadContext(aContext.iNode,aContext.iNodeActivity,ctxBuf,msgBuf,dummy);
if (storedContext)
{
ClearWillWait();
if (IsTransition())
{
//Transition stored
aContext.iNodeActivity->ACore::DoCurrent(storedContext);
}
else
{
//This is the state stored
#ifdef _DEBUG
TBool hasRun = aContext.iNodeActivity->ACore::Next(storedContext);
__ASSERT_DEBUG(hasRun!=(iFlags & KWillWait), User::Panic(KSpecAssert_ElemMeshMachActC, 13));
#else
aContext.iNodeActivity->ACore::Next(storedContext);
#endif
}
if (!(iFlags & KWillWait))
{ //We have been run
ClearIsWaiting();
iContextDesc.Zero();
aContext.Node().HandleMessageReturnValue(*storedContext);
return ETrue;
}
}
return EFalse;
}