telephonyserverplugins/ctsydispatchlayer/test/ltsyskeleton_using_dispatcher/ltsyskeleton/src/creceivethreadmanager.cpp
// Copyright (c) 2008-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:
//
#include "creceivethreadmanager.h"
#include <ctsy/ltsy/ltsylogger.h>
CReceiveThreadManager* CReceiveThreadManager::NewL(MLtsyReceiveThreadEventStateHandler& aEventHandler, MLtsyReceiveThreadObserver& aLifeObserver)
{
CReceiveThreadManager* self = new(ELeave) CReceiveThreadManager();
CleanupStack::PushL(self);
self->ConstructL(aEventHandler, aLifeObserver);
CleanupStack::Pop(self);
return self;
}
CReceiveThreadManager::~CReceiveThreadManager()
{
StopReceiveThread();
delete iThreadLifeWatcher;
delete iReceiveThreadWaitLoopSignaller;
}
CReceiveThreadManager::CReceiveThreadManager()
{
}
void CReceiveThreadManager::ConstructL(MLtsyReceiveThreadEventStateHandler& aEventHandler, MLtsyReceiveThreadObserver& aLifeObserver)
{
iThreadLifeWatcher = CReceiveThreadLifeWatcher::NewL(aLifeObserver);
iReceiveThreadWaitLoopSignaller = CReceiveThreadWaitLoopSignaller::NewL(aEventHandler);
}
void CReceiveThreadManager::StartReceiveThreadL()
{
SpawnReceiveThreadL();
}
void CReceiveThreadManager::SpawnReceiveThreadL()
{
TSYLOGENTRYEXIT;
//These may need chsanging for specific LTSY requirements
const TInt KStackSize=0x8000;
const TInt KHeapSize=0x8000;
const TInt KMaxHeapSize=0x80000;
_LIT(KThreadName,"LtsyReceiveThread");
User::LeaveIfError(iThread.Create(KThreadName,StartReceiveThreadFn,
KStackSize,KHeapSize,KMaxHeapSize,iReceiveThreadWaitLoopSignaller));
iReceiveThreadWaitLoopSignaller->StartReceiver();
iThreadLifeWatcher->LogonL(iThread.Id());
iThread.Resume();
//NOTE: We don't bother rendevous'ing and rely on the logon to complete #
//if thread does not start correctly.
}
TInt CReceiveThreadManager::StartReceiveThreadFn(TAny* aArg)
{
LOG(_L8("StartReceiveThreadFn, thread id = %d"), RThread().Id().Id());
// Create a cleanup stack object
CTrapCleanup* cleanup=CTrapCleanup::New();
if (cleanup==NULL)
return KErrNoMemory;
//start the wait loop
CReceiveThreadWaitLoopSignaller* signaller = static_cast<CReceiveThreadWaitLoopSignaller*>(aArg);
TInt err = signaller->WaitLoop();
delete cleanup;
LOG(_L8("StartReceiveThreadFn, thread id = %d, Terminated with err = %d"), RThread().Id().Id(), err);
return err;
}
void CReceiveThreadManager::StopReceiveThread()
{
TSYLOGENTRYEXIT;
/*
* There are two ways to stop a thread
* 1. Use RThread::Kill
* 2. Post a request that is handled the next time the thread runs.
*
* Problem wth 2. is that it is indeterminate the time before the thread will run. For us,
* it will run the next time an event is received from the baseband.
*
* Kill is less graceful but if StopReceiveThread has been called
* chances are we are on our way to a reboot anyway!
*/
iThread.Kill(KErrNone);
iThread.Close();
}
//
CReceiveThreadLifeWatcher* CReceiveThreadLifeWatcher::NewL(MLtsyReceiveThreadObserver& aReceiveThreadLifeObserver)
{
CReceiveThreadLifeWatcher* self = new(ELeave) CReceiveThreadLifeWatcher(aReceiveThreadLifeObserver);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop(self);
return self;
}
CReceiveThreadLifeWatcher::CReceiveThreadLifeWatcher(MLtsyReceiveThreadObserver& aReceiveThreadLifeObserver)
: CActive(EPriorityHigh), iReceiveThreadLifeObserver(aReceiveThreadLifeObserver) //use high priority to get quicker scheduling of AO when event triggered
{
CActiveScheduler::Add(this);
}
CReceiveThreadLifeWatcher::~CReceiveThreadLifeWatcher()
{
TSYLOGENTRYEXIT;
Cancel();
iThread.Close();
}
void CReceiveThreadLifeWatcher::ConstructL()
{
}
void CReceiveThreadLifeWatcher::LogonL(TThreadId aID)
{
iThread.Close();
User::LeaveIfError(iThread.Open(aID));
iThread.Logon(iStatus);
SetActive();
LOG(_L8("CReceiveThreadLifeWatcher::LogonL. Watching thread id = %d"), iThread.Id().Id());
}
void CReceiveThreadLifeWatcher::RunL()
{
TSYLOGENTRYEXIT;
LOG(_L8("CReceiveThreadLifeWatcher::RunL iThread = %d, iStatus=%d, exit reason = %d"), iThread.Id().Id(), iStatus.Int(), iThread.ExitReason() );
iReceiveThreadLifeObserver.HandleReceiveThreadLifeWatcherComplete(iStatus.Int());
}
void CReceiveThreadLifeWatcher::DoCancel()
{
iThread.LogonCancel(iStatus);
}
//
CReceiveThreadWaitLoopSignaller* CReceiveThreadWaitLoopSignaller::NewL(MLtsyReceiveThreadEventStateHandler& aEventHandler)
{
CReceiveThreadWaitLoopSignaller* self = new(ELeave) CReceiveThreadWaitLoopSignaller(aEventHandler);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop(self);
return self;
}
CReceiveThreadWaitLoopSignaller::CReceiveThreadWaitLoopSignaller(MLtsyReceiveThreadEventStateHandler& aEventHandler)
: CActive(EPriorityNormal), iEventHandler(aEventHandler)
{
CActiveScheduler::Add(this);
}
CReceiveThreadWaitLoopSignaller::~CReceiveThreadWaitLoopSignaller()
{
TSYLOGENTRYEXIT;
Cancel();
iEtelThread.Close();
iLtsyReceiveThread.Close();
}
void CReceiveThreadWaitLoopSignaller::ConstructL()
{
// open a reference to the current thread - this will be the thread on which this object
// is created which will be the Etel thread.
User::LeaveIfError(iEtelThread.Open(RThread().Id()));
}
void CReceiveThreadWaitLoopSignaller::StartReceiver()
{
//start listening for completions from the LtsyReceiveThread
iStatus = KRequestPending;
SetActive();
}
void CReceiveThreadWaitLoopSignaller::RunL()
{
TSYLOGENTRYEXIT;
//an event has been signalled from the LtsyReceiveThread - handle it
TInt err = iEventHandler.HandleReceivedEvent();
//re-start listening for next event
StartReceiver();
//signal back to the LtsyReceiveThread that we have completed so it resumes
RequestComplete(iLtsyReceiveThread, iLtsyReceiveThreadReqStatus, err);
}
void CReceiveThreadWaitLoopSignaller::DoCancel()
{
TSYLOGENTRYEXIT;
//only complete if still active otherwise get a stray signal
if(iStatus == KRequestPending) //if request still pending then complete it otherwise event is awaiitng to be consumed by the User::WaitForRequest in the Cancel
RequestComplete(iEtelThread, iStatus, KErrCancel);
}
//This function will be run within the LtsyReceiveThread thread of execution
TInt CReceiveThreadWaitLoopSignaller::WaitLoop()
{
iLtsyReceiveThread.Open(RThread().Id());
TInt err;
//forever loop
do
{
err = KErrNone;
//wait for an event
iEventHandler.DoRequestNextEvent();
//signal event to the other (Etel) thread - this is synchronous.
//We wait for event to be handled and the Etel thread to signal its
//completion before requesting next event. This is because there is no queuing
//on Etel thread
err = ExecSync();
//cleanup - opportunity to free any meory associated with the event
iEventHandler.DoReleaseEvent();
//if ExecSync returned an error we break out the event loop
//this will cause the thread to terminate
if(err == MLtsyReceiveThreadEventStateHandler::KShutdownLtsyReceiveThread)
{
//graceful shutdown
err = KErrNone;
break;
}
else if(err == KErrNone)
{
continue;
}
else
{
//terminate thread
break;
}
}while(ETrue);
return err;
}
TInt CReceiveThreadWaitLoopSignaller::ExecSync()
{
//signal Etel thread
iLtsyReceiveThreadReqStatus = KRequestPending;
//check that ETEL thread is actually waiting for an event
TInt err;
if(IsActive())
{
RequestComplete(iEtelThread, iStatus, KErrNone);
//block awaiting completion from Etel thread to say event has been processed
User::WaitForRequest(iLtsyReceiveThreadReqStatus);
err = iLtsyReceiveThreadReqStatus.Int();
}
else
{
//if ETEL thread not listening we just close as events will be lost otherwise
err = KErrNotReady;
}
return err;
}
void CReceiveThreadWaitLoopSignaller::RequestComplete(RThread& aThread, TRequestStatus& aStatus, TInt aError)
{
TRequestStatus* status = &aStatus;
aThread.RequestComplete(status, aError);
}