libraries/memoryaccess/objectix.cpp
changeset 0 7f656887cf89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libraries/memoryaccess/objectix.cpp	Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,513 @@
+// objectix.cpp
+// 
+// Copyright (c) 1994 - 2010 Accenture. All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the "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:
+// Accenture - Initial contribution
+//
+
+/** 
+@file
+@internalTechnology
+*/
+
+#include <kern_priv.h>
+#include "dobject.h"
+#define DObjectIx DObjectIxNinePointTwoHack // So that we're not redefining DObjectIx
+
+/**	Creates a new kernel object index.
+
+	@param	aPtr	Value to pass to Close() when objects are closed due to index deletion.
+
+	@return	The pointer to the new index, NULL if out of memory.
+
+    @pre    Calling thread must be in a critical section.
+    @pre    No fast mutex can be held.
+	@pre	Call in a thread context.
+ */
+DObjectIx *DObjectIx::New(TAny* aPtr)
+	{
+	return new DObjectIx(aPtr);
+	}
+
+#ifndef DOBJECT_TEST_CODE
+void DObjectIx::Wait()
+	{
+	Kern::MutexWait(*HandleMutex);
+	}
+
+void DObjectIx::Signal()
+	{
+	Kern::MutexSignal(*HandleMutex);
+	}
+#endif
+
+
+/**	Construct a kernel object index
+ */
+DObjectIx::DObjectIx(TAny *aPtr)
+	: iNextInstance(1), iPtr(aPtr), iFree(-1)
+	{
+	}
+
+
+/**	Destroys a kernel object index.
+
+	Any objects in the index are closed. The iPtr member of the index is passed
+	as the parameter to Close() for each object.
+
+    @pre    Calling thread must be in a critical section.
+    @pre    No fast mutex can be held.
+	@pre	Call in a thread context.
+ */
+DObjectIx::~DObjectIx()
+	{
+	// We have to be very careful here. Calling Close() on the objects in the array
+	// may result in other entries being removed from the array before we delete
+	// them here, and may result in the array being ReAlloc()ed, corrupting the removed
+	// entries, hence we must check the iCount value each time round the loop.	
+	TInt i=-1;
+	while(++i<iCount)
+		{
+		SDObjectIxRec* pS=iObjects+i;
+		DObject *pO=pS->obj;
+		if (pO)
+			{
+			--iActiveCount;
+			pS->obj=NULL;	// invalidate entry after closing it
+			pO->Close(iPtr);
+			}
+		}
+	SDObjectIxRec* pR=iObjects;
+	iObjects=NULL;
+	delete pR;
+	}
+
+
+/**	Adds a kernel object to an index and return a handle.
+
+	@param	aObj	Pointer to the object to add
+	@param	aHandle	Place to write returned handle to
+	
+	@return	KErrNone, if operation successful;
+	        KErrNoMemory, if there was insufficient memory to expand the array.
+
+    @pre    Calling thread must be in a critical section.
+    @pre    No fast mutex can be held.
+	@pre	Call in a thread context.
+	@pre	DObject::HandleMutex held
+ */
+TInt DObjectIx::Add(DObject* anObj, TInt& aHandle)
+	{
+	//Check preconditions(debug build only)
+	__ASSERT_CRITICAL;
+	__ASSERT_NO_FAST_MUTEX;
+
+	TInt index=iFree; //iFree contains the index of the first empty slot or -1 if there is no such.
+	if (index<0) //Check if the free list is empty
+		{
+		// The free list is empty, so more slots must be allocated.
+		if (iCount==KObjectIxMaxHandles)
+ 			return KErrNoMemory;
+		
+		//Those are internal checking of the object consistency
+		__ASSERT_DEBUG(iAllocated==iCount,Panic(EObjInconsistent));
+		__ASSERT_DEBUG(iAllocated==iActiveCount,Panic(EObjInconsistent));
+		
+		//Double allocated memory
+		TInt newAlloc=iAllocated ? 2*iAllocated : KObjectIxGranularity;
+		if(newAlloc>KObjectIxMaxHandles)
+ 			newAlloc=KObjectIxMaxHandles;
+		TInt r=Kern::SafeReAlloc((TAny*&)iObjects,iAllocated*sizeof(SDObjectIxRec),newAlloc*sizeof(SDObjectIxRec));
+		if (r)
+			return r;
+		//NOTE: There is no need to initialize newly allocated memory (e.g. to zero iObjects) as it all goes
+		//beyond iCount and will be not considered when search in At(...) or operator[]() methods. 
+		//As the free list is initially ordered, each slot goes through the states as follows:
+		// - Created as the part of the free list beyond iCount. - uninitialized and not searched in any method.
+		// - In use  - initialized.
+		// - The part of the free list within iCount - initialized to zero.
+		//Also bear in mind that UpdateState() does not reorder free list beyond iCount but keeps it preserverd.
+
+		iAllocated=newAlloc;		   //Update the number of allocated slots
+		iUpdateDisabled = iAllocated/2;//The number of Remove() calls before the object update(free list,iCount,..)
+
+		//Connect all newly allocated slots into the list and set 'index' to point to the first one.
+		SDObjectIxRec* pA = (SDObjectIxRec*)iObjects;
+		index=newAlloc-1;
+		pA[index].nextEmpty = -1;
+		while (iCount <= --index) 
+			pA[index].nextEmpty=index+1;
+		index++;
+		}
+
+	//At this point, 'index' variable points to the slot that will be used for the new entry.
+	//It also represents the first element in the list of empty slots.
+
+	SDObjectIxRec *pS=iObjects+index; // pS points to the object that will be used for the new entry.
+	iFree=pS->nextEmpty;			  // Update iFree to point to the next empty slot. 
+	
+	NKern::LockSystem();
+
+	//Initialize data of the new element of the array.
+	pS->obj=anObj;
+	pS->str.uniqueID=(TUint16)anObj->UniqueID();
+	pS->str.instance=(TUint16)instanceLimit(iNextInstance);
+	
+	iNextInstance++;
+	
+	if (index>=iCount)	//Update iCount to points to the slot after the last in use.
+		iCount=index+1;
+	
+	++iActiveCount;
+
+	NKern::UnlockSystem();
+
+	//Internal checking of the object consistency
+	__ASSERT_DEBUG( (iFree==-1 && iAllocated==iCount && iAllocated==iActiveCount)
+				  ||(iFree!=-1 && iAllocated>iActiveCount),Panic(EObjInconsistent));
+	
+	aHandle=makeHandle(index,pS->str.instance);
+	return KErrNone;
+  }
+
+
+/**	Removes a kernel object from an index by handle.
+
+	Returns a pointer to the object removed and the parameter to pass to Close().
+
+	@param	aHandle	Handle to object to be removed.
+	@param	aObj	Place to write pointer to the object to add.
+	@param	aPtr	Place to write parameter to be passed to Close().
+	
+	@return	KErrNone, if operation successful;
+	        KErrBadHandle, if the supplied handle was invalid.
+
+    @pre    Calling thread must be in a critical section.
+    @pre    No fast mutex can be held.
+	@pre	Call in a thread context.
+	@pre	DObject::HandleMutex held.
+ */
+TInt DObjectIx::Remove(TInt aHandle, DObject*& aObj, TAny*& aPtr)
+	{
+	//Check preconditions(debug build only)
+	__ASSERT_CRITICAL;
+	__ASSERT_NO_FAST_MUTEX;
+
+	TInt i=index(aHandle);
+	
+	if (i >= iCount)
+		return KErrBadHandle;
+
+	SDObjectIxRec* pR=iObjects+i;
+	DObject *pO=pR->obj;
+	if (!pO || pR->str.instance!=instance(aHandle) || pR->str.uniqueID!=pO->UniqueID())
+		return KErrBadHandle;
+
+	NKern::LockSystem();
+	pR->obj=NULL;
+	--iActiveCount;
+	NKern::UnlockSystem();
+	
+	if(iActiveCount)
+		{	
+		// Add the entry onto the free list
+		pR->nextEmpty=iFree;
+		iFree=i;
+
+		if(iUpdateDisabled) 
+			iUpdateDisabled--; //Count down till state update is enabled again.
+
+		if (									 //Update the states(HWM, resort free list & memory shrink) if:
+			(!iUpdateDisabled) &&				 //There were a number of Remove() calls since the last ReAlloc
+			(iAllocated>=2*KObjectIxGranularity))//Allocated memory is above the limit.
+			{
+			UpdateState();
+			iUpdateDisabled = iAllocated/2;//The number of Remove() calls before the next update comes.
+			}
+		}
+	else
+		{
+		//There is no any CObject left. Reset the object to initial state (except iNextInstance)
+		delete iObjects;
+		iObjects=NULL;
+		iAllocated=0;
+		iCount=0;
+		iFree=-1;		  //Empty free list
+		}
+
+	//This is internal checking of the object consistency
+	__ASSERT_DEBUG( (iFree==-1 && iAllocated==iCount && iAllocated==iActiveCount)
+				  ||(iFree!=-1 && iAllocated>iActiveCount),Panic(EObjInconsistent));
+	
+	aObj=pO;
+	aPtr=iPtr;
+	return KErrNone;
+  }
+
+
+// Private method which:
+//1. Reorders free list.
+//2. Updates iCount. This is the only place where HWM is decreased, while it can be increased during AddL().
+//3. Shrinks the heap memory (pointed by iObjects) if it can be at least halved.
+//The function is entered with at least one occupied slot in iObjects array.
+//The array is searched from its end. Special care is given to the  case where
+//iCount is less then KObjectIxGranularity as the size of the arrey does not go below it.
+//Note: HighWaterMark (HWM) is a term used for iCount.
+void DObjectIx::UpdateState()
+	{
+	TBool toShrink = EFalse;
+	TBool foundFreeBelowLimit = EFalse;//Used to handle spacial case when HWM is below the minimum alloc. limit
+	TInt newHWM = 0;
+
+	//Start from the HWM as all slots beyond are free and sorted already.
+	TInt current = iCount;
+	TInt prevFreeSlot = iCount == iAllocated ? -1 : iCount;
+	while (--current>=0)
+		{
+		if (iObjects[current].obj)
+			{
+			//This is the slot with the valid entry. Check if this is the last in the array.
+			if(!newHWM)
+				{
+				//This is the first occupied slot we found => It is new HWM.
+				newHWM=current+1;
+				if (current < iAllocated/2)
+					{
+					//At this point we decide to shrink memory.
+					toShrink = ETrue;
+					//Once we find HWM and decide to shrink, all slots after that point should be removed
+					//from the free list as that memory will be freed. The exception is the case when HWM is below
+					//the minimum of allocated memory (8 slots as the moment).
+					if((current >= KObjectIxGranularity) || (!foundFreeBelowLimit))
+						prevFreeSlot = -1; //The next free slot to find will be the last one in the list.
+					}
+				}
+			}
+		else
+			{
+			//This is the free slot.
+			if ((!newHWM) && (!foundFreeBelowLimit) &&(current<KObjectIxGranularity))
+				{
+				//The special case. 
+				//We just reached the first free slot below minimum alloc. size and still we found no occupied slots.
+				iObjects[current].nextEmpty = -1; //This will be the end of free list.
+				foundFreeBelowLimit = ETrue; //Mark that we found the special case
+				}
+			else
+				{
+				iObjects[current].nextEmpty = prevFreeSlot;//Link the empty slot in the free list.
+				}
+			prevFreeSlot = current;
+			}
+		}
+
+	iCount = newHWM;
+	iFree = prevFreeSlot;
+
+	if (toShrink)
+		{
+		//Do not reallocate less then the initial value.
+		TInt newAlloc = Max(newHWM,KObjectIxGranularity);
+		//Update member data and re-allocate memory. ReAlloc cannot return NULL as we are asking for less memory.
+		Kern::SafeReAlloc((TAny*&)iObjects,iAllocated*sizeof(SDObjectIxRec),newAlloc*sizeof(SDObjectIxRec));
+		iAllocated = newAlloc;
+		}
+	}
+
+
+/** Counts the number of times an object appears in this index.
+
+	@param	aObject	Object whose occurrences are to be counted.
+
+	@return	Number of times aObject appears in the index.
+
+    @pre    Calling thread must be in a critical section.
+    @pre    No fast mutex can be held.
+	@pre	Call in a thread context.
+	@pre	DObject::HandleMutex held
+ */
+TInt DObjectIx::Count(DObject* aObject)
+	{
+	//Check preconditions(debug build only)
+	__ASSERT_CRITICAL;
+	__ASSERT_NO_FAST_MUTEX;
+
+	TInt c=0;
+	if (iCount)
+		{
+		SDObjectIxRec* pS=iObjects;
+		SDObjectIxRec* pE=pS+iCount;
+		do
+			{
+			if (pS->obj==aObject)
+				c++;
+			} while (++pS<pE);
+		}
+	return c;
+	}
+
+
+#ifndef __DOBJECT_MACHINE_CODED__
+/**	Looks up an object in the index by handle.
+	
+	The object must be of a specified type (specified by container ID)
+
+	@param	aHandle		Handle to look up.
+	@param	aUniqueID	Unique ID (container ID) that object should have.
+	
+	@return	Pointer to object or NULL if handle invalid.
+
+	@pre	Call in a thread context.
+	@pre    System lock must be held.
+ */
+DObject* DObjectIx::At(TInt aHandle, TInt aUniqueID)
+	{
+	__ASSERT_SYSTEM_LOCK; //Check preconditions (debug build only)
+	TInt i=index(aHandle);
+	if (i>=iCount)
+		return(NULL);
+	SDObjectIxRec *pS=iObjects+i;
+	if (pS->str.instance!=instance(aHandle) || pS->str.uniqueID!=aUniqueID)
+		return(NULL);
+	return(pS->obj);
+	}
+
+
+/**	Looks up an object in the index by handle.
+
+	The object may be of any type.
+
+	@param	aHandle		Handle to look up.
+	
+	@return	Pointer to object or NULL if handle invalid.
+
+	@pre	Call in a thread context.
+	@pre    System lock must be held.
+ */
+DObject* DObjectIx::At(TInt aHandle)
+	{
+	__ASSERT_SYSTEM_LOCK; //Check preconditions (debug build only)
+	TInt i=index(aHandle);
+	if (i>=iCount)
+		return NULL;
+	SDObjectIxRec *pS=iObjects+i;
+	if (pS->str.instance!=instance(aHandle))
+		return NULL;
+	return pS->obj;
+	}
+#endif
+
+
+/**	Looks up an object in the index by object pointer.
+
+	Returns a handle to the object.
+
+	@param	aObj	Pointer to the object to look up.
+	
+	@return	Handle to object (always >0);
+	        KErrNotFound, if object not present in index.
+
+    @pre    Calling thread must be in a critical section.
+    @pre    No fast mutex can be held.
+	@pre	Call in a thread context.
+	@pre	DObject::HandleMutex held.
+ */
+TInt DObjectIx::At(DObject* aObj)
+	{
+	//Check preconditions(debug build only)
+	__ASSERT_CRITICAL;
+	__ASSERT_NO_FAST_MUTEX;
+
+	if (iCount)
+		{
+		SDObjectIxRec* pS=iObjects;
+		SDObjectIxRec* pE=pS+iCount;
+		TInt i=0;
+		while(pS<pE && pS->obj!=aObj)
+			pS++, i++;
+		if (pS<pE)
+			return(makeHandle(i,pS->str.instance));
+		}
+	return KErrNotFound;
+	}
+
+
+#ifndef __DOBJECT_MACHINE_CODED__
+/** Finds the object at a specific position in the index array.
+
+	@param	aIndex	Index into array.
+	
+	@return	Pointer to the object at that position (could be NULL).
+
+	@pre	Call in a thread context. 
+    @pre    System lock must be held.
+ */
+DObject* DObjectIx::operator[](TInt aIndex)
+	{
+	__ASSERT_SYSTEM_LOCK; //Check preconditions (debug build only)
+	__ASSERT_ALWAYS(aIndex>=0 && aIndex<iCount,Panic(EArrayIndexOutOfRange));
+	return iObjects[aIndex].obj;
+	}
+#else
+GLDEF_C void PanicDObjectIxIndexOutOfRange(void)
+	{
+	Panic(EArrayIndexOutOfRange);
+	}
+#endif
+
+TInt DObjectIx::LastHandle()
+//
+// Return the last valid handle
+// Must wait on HandleMutex before calling this.
+//
+	{
+	//Check preconditions(debug build only)
+	__ASSERT_CRITICAL;
+	__ASSERT_NO_FAST_MUTEX;
+
+	if (iActiveCount)
+		{
+		SDObjectIxRec* p=iObjects+iCount;
+		while(--p>=iObjects && !p->obj) {}
+		return makeHandle(p-iObjects,p->str.instance);
+		}
+	return 0;
+	}
+
+//
+// Include these in exactly one CPP file somewhere
+//
+
+extern TBool gRunningWithOldDefinition = ETrue;
+
+NONSHARABLE_CLASS(DObjectWithPaddingOnly) : public DObject
+	{
+public:
+	DOBJECT_PADDING;
+	};
+
+TBool CalculateDObjectSize()
+	{
+	DObjectWithPaddingOnly* obj = new DObjectWithPaddingOnly;
+	if (!obj) return EFalse;
+	
+	// objectId points to the mem location where iObjectId will be. So if running on a system with the new size DOBject it will always be non-zero (because objectIds are set in the DObject constructor, and are always non-zero), but if running on earlier systems it will be zero because DBase zero-fills the object
+	TUint64& objectId = *reinterpret_cast<TUint64*>((TUint8*)&obj->iName + sizeof(HBuf*));
+	
+	if (objectId != 0)
+		{
+		//Kern::Printf("Detected MemoryAccess is running with new larger DObject");
+		gRunningWithOldDefinition = EFalse;
+		}
+	else
+		{
+		//Kern::Printf("Detected MemoryAccess is running with old-format DObject");		
+		}
+	obj->Close(NULL);
+	return ETrue;
+	}