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