commsfwsupport/commselements/meshmachine/src/mm_activities.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 12 Mar 2010 15:49:41 +0200
branchRCL_3
changeset 11 98a7181d2ce7
parent 0 dfb7c4ff071f
child 18 9644881fedd0
permissions -rw-r--r--
Revision: 201008 Kit: 201008

// 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;
    }