libraries/memoryaccess/objectix.cpp
changeset 0 7f656887cf89
equal deleted inserted replaced
-1:000000000000 0:7f656887cf89
       
     1 // objectix.cpp
       
     2 // 
       
     3 // Copyright (c) 1994 - 2010 Accenture. All rights reserved.
       
     4 // This component and the accompanying materials are made available
       
     5 // under the terms of the "Eclipse Public License v1.0"
       
     6 // which accompanies this distribution, and is available
       
     7 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 // 
       
     9 // Initial Contributors:
       
    10 // Accenture - Initial contribution
       
    11 //
       
    12 
       
    13 /** 
       
    14 @file
       
    15 @internalTechnology
       
    16 */
       
    17 
       
    18 #include <kern_priv.h>
       
    19 #include "dobject.h"
       
    20 #define DObjectIx DObjectIxNinePointTwoHack // So that we're not redefining DObjectIx
       
    21 
       
    22 /**	Creates a new kernel object index.
       
    23 
       
    24 	@param	aPtr	Value to pass to Close() when objects are closed due to index deletion.
       
    25 
       
    26 	@return	The pointer to the new index, NULL if out of memory.
       
    27 
       
    28     @pre    Calling thread must be in a critical section.
       
    29     @pre    No fast mutex can be held.
       
    30 	@pre	Call in a thread context.
       
    31  */
       
    32 DObjectIx *DObjectIx::New(TAny* aPtr)
       
    33 	{
       
    34 	return new DObjectIx(aPtr);
       
    35 	}
       
    36 
       
    37 #ifndef DOBJECT_TEST_CODE
       
    38 void DObjectIx::Wait()
       
    39 	{
       
    40 	Kern::MutexWait(*HandleMutex);
       
    41 	}
       
    42 
       
    43 void DObjectIx::Signal()
       
    44 	{
       
    45 	Kern::MutexSignal(*HandleMutex);
       
    46 	}
       
    47 #endif
       
    48 
       
    49 
       
    50 /**	Construct a kernel object index
       
    51  */
       
    52 DObjectIx::DObjectIx(TAny *aPtr)
       
    53 	: iNextInstance(1), iPtr(aPtr), iFree(-1)
       
    54 	{
       
    55 	}
       
    56 
       
    57 
       
    58 /**	Destroys a kernel object index.
       
    59 
       
    60 	Any objects in the index are closed. The iPtr member of the index is passed
       
    61 	as the parameter to Close() for each object.
       
    62 
       
    63     @pre    Calling thread must be in a critical section.
       
    64     @pre    No fast mutex can be held.
       
    65 	@pre	Call in a thread context.
       
    66  */
       
    67 DObjectIx::~DObjectIx()
       
    68 	{
       
    69 	// We have to be very careful here. Calling Close() on the objects in the array
       
    70 	// may result in other entries being removed from the array before we delete
       
    71 	// them here, and may result in the array being ReAlloc()ed, corrupting the removed
       
    72 	// entries, hence we must check the iCount value each time round the loop.	
       
    73 	TInt i=-1;
       
    74 	while(++i<iCount)
       
    75 		{
       
    76 		SDObjectIxRec* pS=iObjects+i;
       
    77 		DObject *pO=pS->obj;
       
    78 		if (pO)
       
    79 			{
       
    80 			--iActiveCount;
       
    81 			pS->obj=NULL;	// invalidate entry after closing it
       
    82 			pO->Close(iPtr);
       
    83 			}
       
    84 		}
       
    85 	SDObjectIxRec* pR=iObjects;
       
    86 	iObjects=NULL;
       
    87 	delete pR;
       
    88 	}
       
    89 
       
    90 
       
    91 /**	Adds a kernel object to an index and return a handle.
       
    92 
       
    93 	@param	aObj	Pointer to the object to add
       
    94 	@param	aHandle	Place to write returned handle to
       
    95 	
       
    96 	@return	KErrNone, if operation successful;
       
    97 	        KErrNoMemory, if there was insufficient memory to expand the array.
       
    98 
       
    99     @pre    Calling thread must be in a critical section.
       
   100     @pre    No fast mutex can be held.
       
   101 	@pre	Call in a thread context.
       
   102 	@pre	DObject::HandleMutex held
       
   103  */
       
   104 TInt DObjectIx::Add(DObject* anObj, TInt& aHandle)
       
   105 	{
       
   106 	//Check preconditions(debug build only)
       
   107 	__ASSERT_CRITICAL;
       
   108 	__ASSERT_NO_FAST_MUTEX;
       
   109 
       
   110 	TInt index=iFree; //iFree contains the index of the first empty slot or -1 if there is no such.
       
   111 	if (index<0) //Check if the free list is empty
       
   112 		{
       
   113 		// The free list is empty, so more slots must be allocated.
       
   114 		if (iCount==KObjectIxMaxHandles)
       
   115  			return KErrNoMemory;
       
   116 		
       
   117 		//Those are internal checking of the object consistency
       
   118 		__ASSERT_DEBUG(iAllocated==iCount,Panic(EObjInconsistent));
       
   119 		__ASSERT_DEBUG(iAllocated==iActiveCount,Panic(EObjInconsistent));
       
   120 		
       
   121 		//Double allocated memory
       
   122 		TInt newAlloc=iAllocated ? 2*iAllocated : KObjectIxGranularity;
       
   123 		if(newAlloc>KObjectIxMaxHandles)
       
   124  			newAlloc=KObjectIxMaxHandles;
       
   125 		TInt r=Kern::SafeReAlloc((TAny*&)iObjects,iAllocated*sizeof(SDObjectIxRec),newAlloc*sizeof(SDObjectIxRec));
       
   126 		if (r)
       
   127 			return r;
       
   128 		//NOTE: There is no need to initialize newly allocated memory (e.g. to zero iObjects) as it all goes
       
   129 		//beyond iCount and will be not considered when search in At(...) or operator[]() methods. 
       
   130 		//As the free list is initially ordered, each slot goes through the states as follows:
       
   131 		// - Created as the part of the free list beyond iCount. - uninitialized and not searched in any method.
       
   132 		// - In use  - initialized.
       
   133 		// - The part of the free list within iCount - initialized to zero.
       
   134 		//Also bear in mind that UpdateState() does not reorder free list beyond iCount but keeps it preserverd.
       
   135 
       
   136 		iAllocated=newAlloc;		   //Update the number of allocated slots
       
   137 		iUpdateDisabled = iAllocated/2;//The number of Remove() calls before the object update(free list,iCount,..)
       
   138 
       
   139 		//Connect all newly allocated slots into the list and set 'index' to point to the first one.
       
   140 		SDObjectIxRec* pA = (SDObjectIxRec*)iObjects;
       
   141 		index=newAlloc-1;
       
   142 		pA[index].nextEmpty = -1;
       
   143 		while (iCount <= --index) 
       
   144 			pA[index].nextEmpty=index+1;
       
   145 		index++;
       
   146 		}
       
   147 
       
   148 	//At this point, 'index' variable points to the slot that will be used for the new entry.
       
   149 	//It also represents the first element in the list of empty slots.
       
   150 
       
   151 	SDObjectIxRec *pS=iObjects+index; // pS points to the object that will be used for the new entry.
       
   152 	iFree=pS->nextEmpty;			  // Update iFree to point to the next empty slot. 
       
   153 	
       
   154 	NKern::LockSystem();
       
   155 
       
   156 	//Initialize data of the new element of the array.
       
   157 	pS->obj=anObj;
       
   158 	pS->str.uniqueID=(TUint16)anObj->UniqueID();
       
   159 	pS->str.instance=(TUint16)instanceLimit(iNextInstance);
       
   160 	
       
   161 	iNextInstance++;
       
   162 	
       
   163 	if (index>=iCount)	//Update iCount to points to the slot after the last in use.
       
   164 		iCount=index+1;
       
   165 	
       
   166 	++iActiveCount;
       
   167 
       
   168 	NKern::UnlockSystem();
       
   169 
       
   170 	//Internal checking of the object consistency
       
   171 	__ASSERT_DEBUG( (iFree==-1 && iAllocated==iCount && iAllocated==iActiveCount)
       
   172 				  ||(iFree!=-1 && iAllocated>iActiveCount),Panic(EObjInconsistent));
       
   173 	
       
   174 	aHandle=makeHandle(index,pS->str.instance);
       
   175 	return KErrNone;
       
   176   }
       
   177 
       
   178 
       
   179 /**	Removes a kernel object from an index by handle.
       
   180 
       
   181 	Returns a pointer to the object removed and the parameter to pass to Close().
       
   182 
       
   183 	@param	aHandle	Handle to object to be removed.
       
   184 	@param	aObj	Place to write pointer to the object to add.
       
   185 	@param	aPtr	Place to write parameter to be passed to Close().
       
   186 	
       
   187 	@return	KErrNone, if operation successful;
       
   188 	        KErrBadHandle, if the supplied handle was invalid.
       
   189 
       
   190     @pre    Calling thread must be in a critical section.
       
   191     @pre    No fast mutex can be held.
       
   192 	@pre	Call in a thread context.
       
   193 	@pre	DObject::HandleMutex held.
       
   194  */
       
   195 TInt DObjectIx::Remove(TInt aHandle, DObject*& aObj, TAny*& aPtr)
       
   196 	{
       
   197 	//Check preconditions(debug build only)
       
   198 	__ASSERT_CRITICAL;
       
   199 	__ASSERT_NO_FAST_MUTEX;
       
   200 
       
   201 	TInt i=index(aHandle);
       
   202 	
       
   203 	if (i >= iCount)
       
   204 		return KErrBadHandle;
       
   205 
       
   206 	SDObjectIxRec* pR=iObjects+i;
       
   207 	DObject *pO=pR->obj;
       
   208 	if (!pO || pR->str.instance!=instance(aHandle) || pR->str.uniqueID!=pO->UniqueID())
       
   209 		return KErrBadHandle;
       
   210 
       
   211 	NKern::LockSystem();
       
   212 	pR->obj=NULL;
       
   213 	--iActiveCount;
       
   214 	NKern::UnlockSystem();
       
   215 	
       
   216 	if(iActiveCount)
       
   217 		{	
       
   218 		// Add the entry onto the free list
       
   219 		pR->nextEmpty=iFree;
       
   220 		iFree=i;
       
   221 
       
   222 		if(iUpdateDisabled) 
       
   223 			iUpdateDisabled--; //Count down till state update is enabled again.
       
   224 
       
   225 		if (									 //Update the states(HWM, resort free list & memory shrink) if:
       
   226 			(!iUpdateDisabled) &&				 //There were a number of Remove() calls since the last ReAlloc
       
   227 			(iAllocated>=2*KObjectIxGranularity))//Allocated memory is above the limit.
       
   228 			{
       
   229 			UpdateState();
       
   230 			iUpdateDisabled = iAllocated/2;//The number of Remove() calls before the next update comes.
       
   231 			}
       
   232 		}
       
   233 	else
       
   234 		{
       
   235 		//There is no any CObject left. Reset the object to initial state (except iNextInstance)
       
   236 		delete iObjects;
       
   237 		iObjects=NULL;
       
   238 		iAllocated=0;
       
   239 		iCount=0;
       
   240 		iFree=-1;		  //Empty free list
       
   241 		}
       
   242 
       
   243 	//This is internal checking of the object consistency
       
   244 	__ASSERT_DEBUG( (iFree==-1 && iAllocated==iCount && iAllocated==iActiveCount)
       
   245 				  ||(iFree!=-1 && iAllocated>iActiveCount),Panic(EObjInconsistent));
       
   246 	
       
   247 	aObj=pO;
       
   248 	aPtr=iPtr;
       
   249 	return KErrNone;
       
   250   }
       
   251 
       
   252 
       
   253 // Private method which:
       
   254 //1. Reorders free list.
       
   255 //2. Updates iCount. This is the only place where HWM is decreased, while it can be increased during AddL().
       
   256 //3. Shrinks the heap memory (pointed by iObjects) if it can be at least halved.
       
   257 //The function is entered with at least one occupied slot in iObjects array.
       
   258 //The array is searched from its end. Special care is given to the  case where
       
   259 //iCount is less then KObjectIxGranularity as the size of the arrey does not go below it.
       
   260 //Note: HighWaterMark (HWM) is a term used for iCount.
       
   261 void DObjectIx::UpdateState()
       
   262 	{
       
   263 	TBool toShrink = EFalse;
       
   264 	TBool foundFreeBelowLimit = EFalse;//Used to handle spacial case when HWM is below the minimum alloc. limit
       
   265 	TInt newHWM = 0;
       
   266 
       
   267 	//Start from the HWM as all slots beyond are free and sorted already.
       
   268 	TInt current = iCount;
       
   269 	TInt prevFreeSlot = iCount == iAllocated ? -1 : iCount;
       
   270 	while (--current>=0)
       
   271 		{
       
   272 		if (iObjects[current].obj)
       
   273 			{
       
   274 			//This is the slot with the valid entry. Check if this is the last in the array.
       
   275 			if(!newHWM)
       
   276 				{
       
   277 				//This is the first occupied slot we found => It is new HWM.
       
   278 				newHWM=current+1;
       
   279 				if (current < iAllocated/2)
       
   280 					{
       
   281 					//At this point we decide to shrink memory.
       
   282 					toShrink = ETrue;
       
   283 					//Once we find HWM and decide to shrink, all slots after that point should be removed
       
   284 					//from the free list as that memory will be freed. The exception is the case when HWM is below
       
   285 					//the minimum of allocated memory (8 slots as the moment).
       
   286 					if((current >= KObjectIxGranularity) || (!foundFreeBelowLimit))
       
   287 						prevFreeSlot = -1; //The next free slot to find will be the last one in the list.
       
   288 					}
       
   289 				}
       
   290 			}
       
   291 		else
       
   292 			{
       
   293 			//This is the free slot.
       
   294 			if ((!newHWM) && (!foundFreeBelowLimit) &&(current<KObjectIxGranularity))
       
   295 				{
       
   296 				//The special case. 
       
   297 				//We just reached the first free slot below minimum alloc. size and still we found no occupied slots.
       
   298 				iObjects[current].nextEmpty = -1; //This will be the end of free list.
       
   299 				foundFreeBelowLimit = ETrue; //Mark that we found the special case
       
   300 				}
       
   301 			else
       
   302 				{
       
   303 				iObjects[current].nextEmpty = prevFreeSlot;//Link the empty slot in the free list.
       
   304 				}
       
   305 			prevFreeSlot = current;
       
   306 			}
       
   307 		}
       
   308 
       
   309 	iCount = newHWM;
       
   310 	iFree = prevFreeSlot;
       
   311 
       
   312 	if (toShrink)
       
   313 		{
       
   314 		//Do not reallocate less then the initial value.
       
   315 		TInt newAlloc = Max(newHWM,KObjectIxGranularity);
       
   316 		//Update member data and re-allocate memory. ReAlloc cannot return NULL as we are asking for less memory.
       
   317 		Kern::SafeReAlloc((TAny*&)iObjects,iAllocated*sizeof(SDObjectIxRec),newAlloc*sizeof(SDObjectIxRec));
       
   318 		iAllocated = newAlloc;
       
   319 		}
       
   320 	}
       
   321 
       
   322 
       
   323 /** Counts the number of times an object appears in this index.
       
   324 
       
   325 	@param	aObject	Object whose occurrences are to be counted.
       
   326 
       
   327 	@return	Number of times aObject appears in the index.
       
   328 
       
   329     @pre    Calling thread must be in a critical section.
       
   330     @pre    No fast mutex can be held.
       
   331 	@pre	Call in a thread context.
       
   332 	@pre	DObject::HandleMutex held
       
   333  */
       
   334 TInt DObjectIx::Count(DObject* aObject)
       
   335 	{
       
   336 	//Check preconditions(debug build only)
       
   337 	__ASSERT_CRITICAL;
       
   338 	__ASSERT_NO_FAST_MUTEX;
       
   339 
       
   340 	TInt c=0;
       
   341 	if (iCount)
       
   342 		{
       
   343 		SDObjectIxRec* pS=iObjects;
       
   344 		SDObjectIxRec* pE=pS+iCount;
       
   345 		do
       
   346 			{
       
   347 			if (pS->obj==aObject)
       
   348 				c++;
       
   349 			} while (++pS<pE);
       
   350 		}
       
   351 	return c;
       
   352 	}
       
   353 
       
   354 
       
   355 #ifndef __DOBJECT_MACHINE_CODED__
       
   356 /**	Looks up an object in the index by handle.
       
   357 	
       
   358 	The object must be of a specified type (specified by container ID)
       
   359 
       
   360 	@param	aHandle		Handle to look up.
       
   361 	@param	aUniqueID	Unique ID (container ID) that object should have.
       
   362 	
       
   363 	@return	Pointer to object or NULL if handle invalid.
       
   364 
       
   365 	@pre	Call in a thread context.
       
   366 	@pre    System lock must be held.
       
   367  */
       
   368 DObject* DObjectIx::At(TInt aHandle, TInt aUniqueID)
       
   369 	{
       
   370 	__ASSERT_SYSTEM_LOCK; //Check preconditions (debug build only)
       
   371 	TInt i=index(aHandle);
       
   372 	if (i>=iCount)
       
   373 		return(NULL);
       
   374 	SDObjectIxRec *pS=iObjects+i;
       
   375 	if (pS->str.instance!=instance(aHandle) || pS->str.uniqueID!=aUniqueID)
       
   376 		return(NULL);
       
   377 	return(pS->obj);
       
   378 	}
       
   379 
       
   380 
       
   381 /**	Looks up an object in the index by handle.
       
   382 
       
   383 	The object may be of any type.
       
   384 
       
   385 	@param	aHandle		Handle to look up.
       
   386 	
       
   387 	@return	Pointer to object or NULL if handle invalid.
       
   388 
       
   389 	@pre	Call in a thread context.
       
   390 	@pre    System lock must be held.
       
   391  */
       
   392 DObject* DObjectIx::At(TInt aHandle)
       
   393 	{
       
   394 	__ASSERT_SYSTEM_LOCK; //Check preconditions (debug build only)
       
   395 	TInt i=index(aHandle);
       
   396 	if (i>=iCount)
       
   397 		return NULL;
       
   398 	SDObjectIxRec *pS=iObjects+i;
       
   399 	if (pS->str.instance!=instance(aHandle))
       
   400 		return NULL;
       
   401 	return pS->obj;
       
   402 	}
       
   403 #endif
       
   404 
       
   405 
       
   406 /**	Looks up an object in the index by object pointer.
       
   407 
       
   408 	Returns a handle to the object.
       
   409 
       
   410 	@param	aObj	Pointer to the object to look up.
       
   411 	
       
   412 	@return	Handle to object (always >0);
       
   413 	        KErrNotFound, if object not present in index.
       
   414 
       
   415     @pre    Calling thread must be in a critical section.
       
   416     @pre    No fast mutex can be held.
       
   417 	@pre	Call in a thread context.
       
   418 	@pre	DObject::HandleMutex held.
       
   419  */
       
   420 TInt DObjectIx::At(DObject* aObj)
       
   421 	{
       
   422 	//Check preconditions(debug build only)
       
   423 	__ASSERT_CRITICAL;
       
   424 	__ASSERT_NO_FAST_MUTEX;
       
   425 
       
   426 	if (iCount)
       
   427 		{
       
   428 		SDObjectIxRec* pS=iObjects;
       
   429 		SDObjectIxRec* pE=pS+iCount;
       
   430 		TInt i=0;
       
   431 		while(pS<pE && pS->obj!=aObj)
       
   432 			pS++, i++;
       
   433 		if (pS<pE)
       
   434 			return(makeHandle(i,pS->str.instance));
       
   435 		}
       
   436 	return KErrNotFound;
       
   437 	}
       
   438 
       
   439 
       
   440 #ifndef __DOBJECT_MACHINE_CODED__
       
   441 /** Finds the object at a specific position in the index array.
       
   442 
       
   443 	@param	aIndex	Index into array.
       
   444 	
       
   445 	@return	Pointer to the object at that position (could be NULL).
       
   446 
       
   447 	@pre	Call in a thread context. 
       
   448     @pre    System lock must be held.
       
   449  */
       
   450 DObject* DObjectIx::operator[](TInt aIndex)
       
   451 	{
       
   452 	__ASSERT_SYSTEM_LOCK; //Check preconditions (debug build only)
       
   453 	__ASSERT_ALWAYS(aIndex>=0 && aIndex<iCount,Panic(EArrayIndexOutOfRange));
       
   454 	return iObjects[aIndex].obj;
       
   455 	}
       
   456 #else
       
   457 GLDEF_C void PanicDObjectIxIndexOutOfRange(void)
       
   458 	{
       
   459 	Panic(EArrayIndexOutOfRange);
       
   460 	}
       
   461 #endif
       
   462 
       
   463 TInt DObjectIx::LastHandle()
       
   464 //
       
   465 // Return the last valid handle
       
   466 // Must wait on HandleMutex before calling this.
       
   467 //
       
   468 	{
       
   469 	//Check preconditions(debug build only)
       
   470 	__ASSERT_CRITICAL;
       
   471 	__ASSERT_NO_FAST_MUTEX;
       
   472 
       
   473 	if (iActiveCount)
       
   474 		{
       
   475 		SDObjectIxRec* p=iObjects+iCount;
       
   476 		while(--p>=iObjects && !p->obj) {}
       
   477 		return makeHandle(p-iObjects,p->str.instance);
       
   478 		}
       
   479 	return 0;
       
   480 	}
       
   481 
       
   482 //
       
   483 // Include these in exactly one CPP file somewhere
       
   484 //
       
   485 
       
   486 extern TBool gRunningWithOldDefinition = ETrue;
       
   487 
       
   488 NONSHARABLE_CLASS(DObjectWithPaddingOnly) : public DObject
       
   489 	{
       
   490 public:
       
   491 	DOBJECT_PADDING;
       
   492 	};
       
   493 
       
   494 TBool CalculateDObjectSize()
       
   495 	{
       
   496 	DObjectWithPaddingOnly* obj = new DObjectWithPaddingOnly;
       
   497 	if (!obj) return EFalse;
       
   498 	
       
   499 	// 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
       
   500 	TUint64& objectId = *reinterpret_cast<TUint64*>((TUint8*)&obj->iName + sizeof(HBuf*));
       
   501 	
       
   502 	if (objectId != 0)
       
   503 		{
       
   504 		//Kern::Printf("Detected MemoryAccess is running with new larger DObject");
       
   505 		gRunningWithOldDefinition = EFalse;
       
   506 		}
       
   507 	else
       
   508 		{
       
   509 		//Kern::Printf("Detected MemoryAccess is running with old-format DObject");		
       
   510 		}
       
   511 	obj->Close(NULL);
       
   512 	return ETrue;
       
   513 	}