// Copyright (c) 1994-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "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:
// e32\kernel\sprocess.cpp
//
//
#include <kernel/kern_priv.h>
#include <e32uid.h>
#define iMState iWaitLink.iSpare1
_LIT(KDollarLock,"$LOCK");
_LIT(KDllDollarLock,"DLL$LOCK");
_LIT(KLitMain,"Main");
_LIT(KLitKill,"Kill");
_LIT(KLitTerminate,"Terminate");
/********************************************
* Process
********************************************/
DProcess::DProcess()
: iPriority(EProcPriorityForeground),
iExitType((TUint8)EExitPending), iGeneration(1), iFlags(KProcessFlagJustInTime),
iDynamicCode(8, _FOFF(SCodeSegEntry, iSeg), 2*256)
{
//reserve slot 0 for later use for the command line
iEnvironmentData[0] = EBinaryData;
}
DProcess::~DProcess()
{
delete iCommandLine;
//delete any process parameters given to this process when it was created
TInt i;
for (i = 0; i < KArgIndex; ++i)
{
//if the value is a pointer to a buffer, delete it
//if its a pointer to an object, close it
TInt data = iEnvironmentData[i]&~3;
if (iEnvironmentData[i] & EBinaryData)
{
delete (HBuf8*)(data);
}
else if (iEnvironmentData[i] & EHandle)
{
((DObject*)data)->Close(NULL);
}
}
}
void DProcess::Destruct()
{
}
void DProcess::DoAppendName(TDes& aName)
{
DObject::DoAppendName(aName);
aName.Append('[');
aName.AppendNumFixedWidth(iUids.iUid[2].iUid,EHex,8);
aName.Append(']');
aName.AppendNumFixedWidth(iGeneration,EDecimal,4);
}
TInt NextGeneration(const TDesC& aName, TUid aUid)
//
// Return the next generation number for the process
//
{
const TInt KNameSuffixLen = 14; // Number of chars after root name, see DoAppendName
__KTRACE_OPT(KPROC,Kern::Printf("DProcess::NextGeneration %S 0x%08x", &aName, aUid.iUid));
TInt gen=0;
DObjectCon& processes=*K::Containers[EProcess];
TKName name;
processes.Wait();
for (TInt i = 0 ; i < processes.Count() ; ++i)
{
DProcess* pP=(DProcess*)processes[i];
if (pP->iAccessCount > 0 && aUid.iUid == pP->iUids.iUid[2].iUid && pP->NameBuf())
{
pP->Name(name);
if (name.Length() > KNameSuffixLen)
{
TPtrC rootName = name.Left(name.Length() - KNameSuffixLen);
if (aName.CompareF(rootName) == 0 && pP->iAccessCount > 0)
{
if (pP->iGeneration>gen)
gen=pP->iGeneration;
}
}
}
}
processes.Signal();
__KTRACE_OPT(KPROC,Kern::Printf("DProcess generation %d",gen+1));
return(gen+1);
}
__ASSERT_COMPILE(ECapability_Limit<=32); // Kernel's iCaps caps below assumes this
TInt DProcess::SetPaging(const TProcessCreateInfo& aInfo)
{// Default implementation that only verifies flags, this virtual method
// is overridden in memory models that support paging.
if (aInfo.iFlags & TProcessCreateInfo::EDataPagingMask ==
TProcessCreateInfo::EDataPagingMask)
{
return KErrCorrupt;
}
return KErrNone;
}
TInt DProcess::Create(TBool aKernelProcess, TProcessCreateInfo& aInfo, HBuf* aCommandLine)
{
__KTRACE_OPT(KBOOT,Kern::Printf("DProcess::Create"));
TInt r=KErrNone;
iCommandLine=aCommandLine; // do this first to prevent memory leak if error occurs
TPtrC f=aInfo.iFileName.Mid(aInfo.iRootNameOffset,aInfo.iRootNameLength);
r=SetName(&f);
if (r!=KErrNone)
return r;
// Verify and save any data paging attributes.
SetPaging(aInfo);
iUids=aInfo.iUids;
iS=aInfo.iS;
iDebugAttributes = (TUint8)aInfo.iDebugAttributes;
if (!aKernelProcess)
{
iPriority=0;
TInt p=ConvertPriority(aInfo.iPriority);
if (p<0)
return KErrArgument;
iPriority=p;
iGeneration=NextGeneration(f,iUids.iUid[2]);
}
else
{
iPriority = EProcPrioritySystemServer3;
iFlags |= KProcessFlagSystemPermanent|KProcessFlagSystemCritical;
}
#ifdef BTRACE_THREAD_PRIORITY
BTrace8(BTrace::EThreadPriority,BTrace::EProcessPriority,this,iPriority);
#endif
iId = K::NewId();
iCreatorId = iId; // Initialise as self for safety because creator has special capabilities
if(TheSuperPage().KernelConfigFlags() & EKernelConfigPlatSecProcessIsolation)
{
if(aInfo.iSecurityZone==KSecurityZoneUnique)
iSecurityZone=iId;
else
iSecurityZone=aInfo.iSecurityZone;
}
else
iSecurityZone=KSecurityZoneLegacyCode;
r=K::MutexCreate((DMutex*&)iProcessLock, KDollarLock, this, EFalse, KMutexOrdProcessLock);
__KTRACE_OPT(KPROC,Kern::Printf("Lock mutex created, %d",r));
if (r!=KErrNone)
return r;
if (!aKernelProcess)
{
r=K::MutexCreate((DMutex*&)iDllLock, KDllDollarLock, this, EFalse, KMutexOrdUser);
if (r!=KErrNone)
return r;
}
r=DoCreate(aKernelProcess,aInfo);
if (r!=KErrNone)
return r;
__KTRACE_OPT(KPROC,Kern::Printf("Process attributes %08x",iAttributes));
iAttributes |= EBeingLoaded;
#ifdef __DEBUGGER_SUPPORT__
// Send new process notification. Note that the creator thread can not always
// be figured out. It could have been killed atfer requesting the loader to
// create the process or the process could be created by a kernel thread.
NKern::LockSystem();
DThread* creator = (DThread*)TheCurrentThread->ObjectFromHandle(aInfo.iClientHandle,EThread);
if (creator && (creator->Open() != KErrNone))
creator = NULL;
NKern::UnlockSystem();
__DEBUG_EVENT2(EEventAddProcess, this, creator);
if (creator)
creator->Close(NULL);
#endif
#ifdef BTRACE_THREAD_IDENTIFICATION
BTrace4(BTrace::EThreadIdentification,BTrace::EProcessCreate,this);
#endif
iTempCodeSeg=(DCodeSeg*)aInfo.iHandle;
if (iTempCodeSeg)
{
if (iTempCodeSeg->iExeCodeSeg!=iTempCodeSeg)
return KErrNotSupported;
iTempCodeSeg->WaitCheckedOpen();
r=AttachExistingCodeSeg(aInfo); // doesn't map the code in - Loaded() does this
if (r!=KErrNone)
return r;
}
else
{
iTempCodeSeg=M::NewCodeSeg(aInfo);
if (!iTempCodeSeg)
return KErrNoMemory;
iTempCodeSeg->iExeCodeSeg=iTempCodeSeg;
r=iTempCodeSeg->Create(aInfo,this);
if (r!=KErrNone)
return r;
iTempCodeSeg->WaitCheckedOpen();
aInfo.iHandle=iTempCodeSeg;
}
if (!aKernelProcess)
{
DThread* pT=NULL;
SStdEpocThreadCreateInfo t;
t.iType=EThreadUser;
t.iFunction=(TThreadFunction)aInfo.iFileEntryPoint; // kernel will call veneer, veneer will call this
t.iPtr=NULL;
t.iSupervisorStack=NULL;
t.iSupervisorStackSize=0; // zero means use default value
t.iUserStack=NULL;
t.iUserStackSize=aInfo.iStackSize;
t.iInitialThreadPriority=EThrdPriorityNormal;
t.iName.Set(KLitMain);
t.iAllocator=NULL;
t.iHeapInitialSize=aInfo.iHeapSizeMin;
t.iHeapMaxSize=aInfo.iHeapSizeMax;
t.iTotalSize = sizeof(t);
r=NewThread((DThread*&)pT, t, NULL, EOwnerProcess);
if (r==KErrNone)
pT->iFlags |= KThreadFlagProcessPermanent|KThreadFlagOriginal;
iUserThreadsRunning=1;
}
if (r!=KErrNone)
return r;
if (!aKernelProcess)
r=K::AddObject(this,EProcess);
return r;
}
DCodeSeg* DProcess::CodeSeg()
{
return iCodeSeg ? iCodeSeg : iTempCodeSeg;
}
TInt DProcess::SetPriority(TProcessPriority aPriority)
{
TInt p=ConvertPriority(aPriority);
if (p<0)
return KErrArgument;
if (iExitType!=EExitPending)
return KErrDied;
__KTRACE_OPT(KPROC,Kern::Printf("Process %O SetPriority(%d)",this,p));
TInt r=WaitProcessLock();
__KTRACE_FAIL(r,Kern::Printf("PSP: %d",r));
if (r!=KErrNone)
return KErrDied;
#ifdef BTRACE_THREAD_PRIORITY
BTrace8(BTrace::EThreadPriority,BTrace::EProcessPriority,this,p);
#endif
NKern::LockSystem();
iPriority=p;
SDblQueLink* pLink=iThreadQ.First();
while (pLink!=&iThreadQ.iA)
{
DThread* pT=_LOFF(pLink,DThread,iProcessLink);
pT->SetThreadPriority(pT->iThreadPriority);
pLink=pLink->iNext;
NKern::FlashSystem();
}
NKern::UnlockSystem();
SignalProcessLock();
return KErrNone;
}
TInt DProcess::ConvertPriority(TProcessPriority aPriority)
{
TInt p=-1;
switch(aPriority)
{
case EPriorityLow: p=EProcPriorityLow; break;
case EPriorityBackground: p=EProcPriorityBackground; break;
case EPriorityForeground: p=EProcPriorityForeground; break;
case EPriorityHigh: p=EProcPriorityHigh; break;
case EPriorityWindowServer: p=EProcPrioritySystemServer1; break;
case EPriorityFileServer: p=EProcPrioritySystemServer2; break;
case EPriorityRealTimeServer: p=EProcPriorityRealTimeServer; break;
case EPrioritySupervisor: p=EProcPrioritySystemServer3; break;
}
return p;
}
TInt DProcess::Rename(const TDesC& aName)
{
if (aName.Length()>KMaxKernelName-KMaxUidName-4)
return KErrBadName;
TKName n;
DObject::BaseName(n); // get current name, without UID and generation number
if (n.MatchF(aName)==0)
return KErrNone; // new name is the same so nothing to do
iGeneration = NextGeneration(aName, iUids.iUid[2]);
__KTRACE_OPT(KTHREAD,Kern::Printf("DProcess::Rename %O to %lS",this,&aName));
TInt r = SetName(&aName);
#ifdef BTRACE_THREAD_IDENTIFICATION
Name(n);
BTraceN(BTrace::EThreadIdentification,BTrace::EProcessName,0,this,n.Ptr(),n.Size());
#endif
__COND_DEBUG_EVENT(r==KErrNone, EEventUpdateProcess, this);
return(r);
}
void DProcess::Release()
{
#ifdef __SMP__
// delete thread group
if (iSMPUnsafeGroup)
{
NKern::GroupDestroy(iSMPUnsafeGroup);
Kern::Free(iSMPUnsafeGroup);
}
#endif
// delete handles with lock mutex free
__KTRACE_OPT(KPROC,Kern::Printf("Process %O Release()",this));
__KTRACE_OPT(KPROC,Kern::Printf("Deleting handles"));
iHandles.Close(this);
// Notify process unloading before code segs are released
__DEBUG_EVENT(EEventUnloadingProcess, this);
__NK_ASSERT_DEBUG(iGarbageList.IsEmpty());
if (iCodeSeg || iTempCodeSeg)
{
DCodeSeg::Wait();
if (iCodeSeg)
RemoveCodeSeg(iCodeSeg, NULL);
DCodeSeg* pS = (DCodeSeg*)__e32_atomic_swp_ord_ptr(&iTempCodeSeg, 0);
if (pS)
pS->CheckedClose();
RemoveDllData();
DCodeSeg::Signal();
}
__ASSERT_ALWAYS(iDynamicCode.Count()==0, K::Fault(K::ECodeStillMapped));
iDynamicCode.Close();
__KTRACE_OPT(KPROC,Kern::Printf("Closing DLL$LOCK mutex"));
Kern::SafeClose((DObject*&)iDllLock,this);
__KTRACE_OPT(KPROC,Kern::Printf("Closing owning process"));
Kern::SafeClose((DObject*&)iOwningProcess,NULL);
__KTRACE_OPT(KPROC,Kern::Printf("Closing DataBssStack chunk"));
Kern::SafeClose((DObject*&)iDataBssStackChunk,this);
NKern::LockSystem();
iFinalReleaseFlag = ETrue;
if (iProcessLock)
iProcessLock->Wait(); // this will get released when the lock mutex is deleted
NKern::UnlockSystem();
__KTRACE_OPT(KPROC,Kern::Printf("Calling FinalRelease()"));
// Call FinalRelease() with process $LOCK mutex held (if it exists)
// and don't ever release the mutex afterwards.
FinalRelease();
__KTRACE_OPT(KPROC,Kern::Printf("Closing $LOCK mutex"));
Kern::SafeClose((DObject*&)iProcessLock,this);
// Notify process removal before closing process
__DEBUG_EVENT(EEventRemoveProcess, this);
#ifdef BTRACE_THREAD_IDENTIFICATION
BTrace4(BTrace::EThreadIdentification,BTrace::EProcessDestroy,this);
#endif
// Kill any processes which we created but have not resumed
NKern::LockSystem(); // In case we died in the middle of DProcess::Resume()
NKern::UnlockSystem();
for(;;)
{
DObjectCon& processes=*K::Containers[EProcess];
processes.Wait();
DProcess* zombie=NULL;
TInt c=processes.Count();
for (TInt i=0; i<c; i++)
{
DProcess* pP=(DProcess*)processes[i];
if (pP->iCreatorId==iId && pP!=this && pP->Open()==KErrNone)
{
zombie = pP;
break;
}
}
processes.Signal();
if(!zombie)
break;
__KTRACE_OPT(KPROC,Kern::Printf("Killing zombie process %O",zombie));
zombie->iCreatorId=zombie->iId;
zombie->Die(EExitPanic,EZombieProcessKilled,KLitKernExec());
zombie->Close(0);
}
__KTRACE_OPT(KPROC,Kern::Printf("Closing process"));
Close(this);
}
TInt DProcess::WaitProcessLock()
{
TInt r=KErrGeneral;
NKern::LockSystem();
if (iProcessLock && !iFinalReleaseFlag) // iProcessLock is deleted during process death
{
r=iProcessLock->Wait(); // mutex may be resetting just before deletion
__KTRACE_FAIL(r,Kern::Printf("PLW: %d",r)); // will return KErrGeneral if mutex is reset
}
NKern::UnlockSystem();
__KTRACE_FAIL(r,Kern::Printf("WPL: %d",r));
return r;
}
TInt DProcess::SignalProcessLock()
{
TInt r=KErrDied;
NKern::LockSystem();
if (iProcessLock)
{
iProcessLock->Signal();
r=KErrNone;
}
else
NKern::UnlockSystem();
__KTRACE_FAIL(r,Kern::Printf("SPL: %d",r));
return r;
}
TInt DProcess::Loaded(TProcessCreateInfo& aInfo)
//
// Confirm that the process has been loaded O.K.
//
{
__KTRACE_OPT(KPROC,Kern::Printf("DProcess::Loaded"));
//
// Update our code segment and dependents
//
DCodeSeg::Wait();
TInt r = KErrNone;
if (!(iTempCodeSeg->iMark & DCodeSeg::EMarkLoaded))
{
// newly loaded code segment, not another instance of same
r = iTempCodeSeg->Loaded(aInfo);
}
if (r == KErrNone)
r = OpenDeps();
DCodeSeg::Signal();
if (r != KErrNone)
return r;
if (iCodeSeg->iAttr & ECodeSegAttSMPSafe)
iSMPUnsafeCount = 0;
else
{
iSMPUnsafeCount = 1;
#ifdef __SMP__
r = UpdateSMPSafe();
if (r != KErrNone)
return r;
#endif
}
FirstThread()->iMState=DThread::EReady;
SetProtection(DObject::EGlobal);
iAttributes &= ~EBeingLoaded;
iReentryPoint = iCodeSeg->iEntryPtVeneer;
// Send loaded process notification.
__DEBUG_EVENT(EEventLoadedProcess, this);
return KErrNone;
}
void DProcess::Resume()
//
// Resume a newly created process. Idempotent. Enter and return with system locked.
//
{
if (iAttributes&EBeingLoaded)
K::PanicCurrentThread(EProcessNotLoaded);
if (!(iAttributes&EResumed))
{
iAttributes|=EResumed;
iCreatorId = iId; // Creator loses control over process once it has been resumed
FirstThread()->Resume();
}
}
void DProcess::Die(TExitType aType, TInt aReason, const TDesC &aCategory)
//
// Kill a process. Enter and return with system unlocked and calling thread in critical section.
//
{
__KTRACE_OPT(KPROC,Kern::Printf("Process %O Die: %d %d %lS",this,aType,aReason,&aCategory));
TInt r=WaitProcessLock();
if (r!=KErrNone)
return; // process already exiting
NKern::LockSystem();
if (iAttributes&EBeingLoaded)
{
// Load failed before UserSvr::ProcessLoaded() was called.
// The thread is still runnable however, since UserSvr::ProcessCreate() must have succeeded.
// We just need to set the thread state so it can be resumed.
FirstThread()->iMState=DThread::EReady;
}
if (iExitType==EExitPending)
{
iExitType = (TUint8)aType;
iExitReason=aReason;
if (iExitType==EExitKill)
iExitCategory=KLitKill;
else if (iExitType==EExitTerminate)
iExitCategory=KLitTerminate;
else
iExitCategory=aCategory;
if (iExitType!=EExitKill && (iFlags & (KProcessFlagSystemPermanent|KProcessFlagSystemCritical)))
K::Fault(K::ESystemProcessPanic);
if (iFlags & KProcessFlagSystemPermanent)
K::Fault(K::EPermanentProcessExit);
// Kill all threads; when the last one exits the process will be cleaned up.
TBool killMe = KillAllThreads(aType, aReason, aCategory);
if (killMe)
{
DThread* pC=TheCurrentThread;
pC->iExitType=(TUint8)iExitType;
pC->iExitReason=iExitReason;
pC->iExitCategory=iExitCategory;
NKern::DeferredExit();
}
}
NKern::UnlockSystem();
SignalProcessLock();
}
TBool DProcess::KillAllThreads(TExitType aType, TInt aReason, const TDesC &aCategory)
//
// Kill all threads in a process. Enter and return with system locked, the process lock held and
// calling thread in a critical section. Returns whether the current thread needs to exit too.
//
{
TBool killMe=EFalse;
DThread* pC=TheCurrentThread;
SDblQueLink* pLink=iThreadQ.First();
while (pLink!=&iThreadQ.iA)
{
DThread* pT=_LOFF(pLink,DThread,iProcessLink);
pLink=pLink->iNext;
if (pT!=pC)
{
// Need to stop the current thread being killed as a consequence of killing pT
pT->iFlags &= ~(KThreadFlagProcessPermanent|KThreadFlagProcessCritical);
pT->Die(aType, aReason, aCategory);
}
else
{
killMe=ETrue;
NKern::UnlockSystem();
}
NKern::LockSystem();
}
return killMe;
}
void DProcess::AddThread(DThread &aThread)
{
__KTRACE_OPT(KPROC,Kern::Printf("AddThread %O to %O",&aThread,this));
iThreadQ.Add(&aThread.iProcessLink);
}
TInt DProcess::NewThread(DThread*& aThread, SThreadCreateInfo& anInfo, TInt* aHandle, TOwnerType aType)
{
__KTRACE_OPT(KTHREAD,Kern::Printf("NewThread proc %O, func %08x ptr %08x",this,anInfo.iFunction,anInfo.iPtr));
__KTRACE_OPT(KTHREAD,Kern::Printf("type %d name %lS pri %d",anInfo.iType,&anInfo.iName,anInfo.iInitialThreadPriority));
if (aHandle)
*aHandle=0;
TInt r=GetNewThread(aThread,anInfo);
__KTRACE_FAIL(r,Kern::Printf("GNT: %d",r));
DThread* pT=aThread;
if (r==KErrNone)
{
r=pT->Create(anInfo);
__KTRACE_FAIL(r,Kern::Printf("NTC: %d",r));
if (r==KErrNone && aHandle)
{
r=K::MakeHandleAndOpen(aType,pT,*aHandle);
__KTRACE_FAIL(r,Kern::Printf("NT MHO: %d",r));
}
if (r==KErrNone)
{
r=WaitProcessLock();
if (r==KErrNone)
{
NKern::LockSystem();
if (iExitType==EExitPending)
{
AddThread(*pT);
pT->iMState=DThread::EReady;
// set fully constructed flag here
}
else
r=KErrDied;
NKern::UnlockSystem();
SignalProcessLock();
__KTRACE_FAIL(r,Kern::Printf("NT ADD: %d",r));
}
else
r=KErrDied;
}
}
if (r==KErrNone)
{
if (anInfo.iType == EThreadUser)
{
pT->iUserThreadState = DThread::EUserThreadCreated;
__e32_atomic_tas_ord32(&iUserThreadsRunning, 1, 1, 0);
}
#ifdef BTRACE_THREAD_IDENTIFICATION
if(BTrace::Filter(BTrace::EThreadIdentification))
{
DObjectCon* threads=Kern::Containers()[EThread];
threads->Wait(); // hold mutex so traces below don't get mixed up with other thread creation traces
TKName nameBuf;
Name(nameBuf);
BTraceN(BTrace::EThreadIdentification,BTrace::EProcessName,&pT->iNThread,this,nameBuf.Ptr(),nameBuf.Size());
pT->Name(nameBuf);
BTraceN(BTrace::EThreadIdentification,BTrace::EThreadCreate,&pT->iNThread,this,nameBuf.Ptr(),nameBuf.Size());
BTrace12(BTrace::EThreadIdentification,BTrace::EThreadId,&pT->iNThread,this,pT->iId);
threads->Signal();
}
#endif
__DEBUG_EVENT2(EEventAddThread, pT, TheCurrentThread);
}
else if (pT)
{
if (aHandle && *aHandle)
{
K::HandleClose(*aHandle);
*aHandle=0;
}
pT->Stillborn();
aThread=NULL;
}
__KTRACE_FAIL(r,Kern::Printf("NT: %d",r));
return r;
}
void DProcess::Rendezvous(TInt aReason)
//
// Enter and return with system unlocked and calling thread in critical section.
//
{
TLogon::CompleteAll(iTargetLogons, TLogon::ETargetRendezvous, aReason);
}
TInt DProcess::Logon(TRequestStatus* aStatus, TBool aRendezvous)
{
TInt r = KErrNoMemory;
DThread* pC = TheCurrentThread;
__KTRACE_OPT(KTHREAD, Kern::Printf("Thread %O Logon to process %O, status at %08x rdv=%x",
pC, this, aStatus, aRendezvous));
TLogon* pL = new TLogon;
if (pL)
{
TUint32 type = TLogon::ETargetProcess;
if (aRendezvous)
type |= TLogon::ERendezvous;
r = pL->Attach(iTargetLogons, pC, this, aStatus, type);
if (r != KErrNone)
pL->Close();
}
__KTRACE_OPT(KTHREAD, Kern::Printf("DProcess::Logon ret %d", r));
return r;
}
void DProcess::BTracePrime(TInt aCategory)
{
#ifdef BTRACE_THREAD_IDENTIFICATION
if(aCategory==BTrace::EThreadIdentification || aCategory==-1)
BTrace4(BTrace::EThreadIdentification,BTrace::EProcessCreate,this);
#endif
}
#ifdef __SMP__
TInt DProcess::UpdateSMPSafe()
{
TUint32 config = TheSuperPage().KernelConfigFlags();
if (!(config & (EKernelConfigSMPUnsafeCompat|EKernelConfigSMPUnsafeCPU0)) || this == K::TheKernelProcess)
return KErrNone;
__KTRACE_OPT(KPROC,Kern::Printf("Process %O UpdateSMPSafe count=%d",this,iSMPUnsafeCount));
TInt r=WaitProcessLock();
(void)r;
__NK_ASSERT_DEBUG(r==KErrNone);
if ((config & EKernelConfigSMPUnsafeCompat) && !iSMPUnsafeGroup)
{
SNThreadGroupCreateInfo info;
info.iCpuAffinity = KCpuAffinityAny;
iSMPUnsafeGroup = (NThreadGroup*)Kern::Alloc(sizeof(NThreadGroup));
r = KErrNoMemory;
if (iSMPUnsafeGroup)
r = NKern::GroupCreate(iSMPUnsafeGroup, info);
}
if (r==KErrNone)
{
SDblQueLink* pLink=iThreadQ.First();
while (pLink!=&iThreadQ.iA)
{
DThread* pT=_LOFF(pLink,DThread,iProcessLink);
NKern::QueueUserModeCallback(&pT->iNThread, &pT->iSMPSafeCallback);
pLink=pLink->iNext;
}
}
SignalProcessLock();
return r;
}
#endif