|
1 // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // @file |
|
15 // @internalComponent |
|
16 // |
|
17 // |
|
18 |
|
19 #include <e32base.h> |
|
20 #include "cchunkmanager.h" |
|
21 #include "cchunkallocator.h" |
|
22 #include "cmemchunk.h" |
|
23 #include "rchunkpool.h" |
|
24 #include "rmemcell.h" |
|
25 |
|
26 EXPORT_C CChunkManager* CChunkManager::NewL ( TInt aHeapSize ) |
|
27 { |
|
28 CChunkManager* self = new ( ELeave ) CChunkManager ( aHeapSize ); |
|
29 CleanupStack::PushL ( self ); |
|
30 self->ConstructL (); |
|
31 CleanupStack::Pop ( self ); |
|
32 return self; |
|
33 } |
|
34 |
|
35 CChunkManager::CChunkManager ( TInt aHeapSize ) |
|
36 :iHeapSize ( aHeapSize ) |
|
37 { |
|
38 } |
|
39 |
|
40 EXPORT_C CChunkManager::~CChunkManager () |
|
41 { |
|
42 delete iChunkAllocator; |
|
43 for ( TInt i = 0; i < iChunkPools.Count (); i++ ) |
|
44 { |
|
45 iChunkPools[i].Close (); |
|
46 } |
|
47 iChunkPools.Close (); |
|
48 iLock.Close (); |
|
49 } |
|
50 |
|
51 void CChunkManager::ConstructL () |
|
52 { |
|
53 User::LeaveIfError ( iLock.CreateLocal () ); |
|
54 iChunkAllocator = CChunkAllocator::NewL ( iHeapSize ); |
|
55 } |
|
56 |
|
57 CChunkAllocator& CChunkManager::Allocator () |
|
58 { |
|
59 return *iChunkAllocator; |
|
60 } |
|
61 |
|
62 EXPORT_C void CChunkManager::AddPoolL ( TInt aBlockSize, TInt aNumBlocks, TInt aThresholdValue, TInt aMinGrowth ) |
|
63 { |
|
64 __ASSERT_ALWAYS ( aBlockSize > 0, User::Invariant () ); // TODO |
|
65 __ASSERT_ALWAYS ( aNumBlocks > 0, User::Invariant () ); |
|
66 __ASSERT_ALWAYS ( aThresholdValue >= 0, User::Invariant () ); |
|
67 __ASSERT_ALWAYS ( aMinGrowth >= 0, User::Invariant () ); |
|
68 |
|
69 // aling the block size |
|
70 const TUint8 KMemAlignment = 8; |
|
71 TInt blockPadding = aBlockSize % KMemAlignment; |
|
72 if ( blockPadding != 0 ) |
|
73 { |
|
74 aBlockSize += KMemAlignment - blockPadding; |
|
75 } |
|
76 if ( aMinGrowth == 0 ) |
|
77 { |
|
78 aMinGrowth = 1; |
|
79 } |
|
80 |
|
81 |
|
82 //maintains iChunkPools in blocks size order |
|
83 TInt found = KErrNotFound; |
|
84 TInt index = 0; |
|
85 RChunkPool tempPool ( *this, aBlockSize ); |
|
86 found = iChunkPools.FindInOrder ( tempPool, index, TLinearOrder<RChunkPool>(RChunkPool::Compare) ); |
|
87 |
|
88 // create a new pool chain |
|
89 if ( found == KErrNotFound ) |
|
90 { |
|
91 RChunkPool pool ( aBlockSize, aThresholdValue, aMinGrowth, *this ); |
|
92 //maintains iChunkPools in blocks size order |
|
93 User::LeaveIfError ( iChunkPools.Insert ( pool, index ) ); |
|
94 |
|
95 RChunkPool* chunkPool = &iChunkPools[index]; |
|
96 chunkPool->OpenL (); |
|
97 |
|
98 // Lock the freelist, inorder to add new blocks to the free list |
|
99 chunkPool->Wait (); |
|
100 TInt err = AllocChunk ( *chunkPool, aNumBlocks ); |
|
101 chunkPool->Signal (); |
|
102 |
|
103 User::LeaveIfError ( err ); |
|
104 } |
|
105 else |
|
106 { |
|
107 // update existing values |
|
108 iChunkPools[found].SetGrowth ( aMinGrowth ); |
|
109 iChunkPools[found].SetThreshold ( aThresholdValue ); |
|
110 } |
|
111 } |
|
112 |
|
113 TInt CChunkManager::AllocChunk ( RChunkPool& aChunkPool, TInt aNumBlocks ) |
|
114 { |
|
115 // verify if maximum size is exceeded |
|
116 if ( ( iUsedSpace + aNumBlocks * aChunkPool.BlockSize () ) > iHeapSize ) |
|
117 { |
|
118 return KErrNoMemory; |
|
119 } |
|
120 |
|
121 CMemChunk* newChunk = CMemChunk::New ( *iChunkAllocator, aChunkPool, aNumBlocks, aChunkPool.BlockSize () ); |
|
122 if ( newChunk != NULL ) |
|
123 { |
|
124 iLock.Wait (); |
|
125 aChunkPool.Append ( newChunk ); |
|
126 iLock.Signal (); |
|
127 |
|
128 // RChunkPool lock is already acquired |
|
129 aChunkPool.AppendToFreeList ( newChunk ); |
|
130 |
|
131 iUsedSpace += sizeof ( CMemChunk ) + ( aNumBlocks * aChunkPool.BlockSize () ) ; |
|
132 return KErrNone; |
|
133 } |
|
134 |
|
135 return KErrNoMemory; |
|
136 } |
|
137 |
|
138 RMemCell* CChunkManager::Alloc ( TInt aSize ) |
|
139 { |
|
140 TInt poolsCount = iChunkPools.Count (); |
|
141 __ASSERT_DEBUG ( poolsCount != 0, User::Invariant () ); //TODO |
|
142 |
|
143 RMemCellList list; |
|
144 // Search and get the avaliable memCell list |
|
145 SearchAndAlloc ( list, aSize ); |
|
146 |
|
147 return list.First (); |
|
148 } |
|
149 |
|
150 void CChunkManager::SearchAndAlloc ( RMemCellList& aList, TInt aSize ) |
|
151 { |
|
152 if ( aSize == 0 ) |
|
153 { |
|
154 __ASSERT_ALWAYS ( 0, User::Invariant () ); //TODO |
|
155 } |
|
156 |
|
157 RChunkPool* poolChosen = NULL; |
|
158 RChunkPool* startPool = &iChunkPools[0]; |
|
159 RChunkPool* pool = &iChunkPools[iChunkPools.Count () - 1]; |
|
160 |
|
161 TInt minUnusedBytes = 0, minBlocks = 0; |
|
162 |
|
163 do |
|
164 { |
|
165 TInt blockSize = pool->BlockSize (); |
|
166 |
|
167 TInt unusedBytes = aSize % blockSize; |
|
168 TInt neededBlocks = aSize / blockSize; |
|
169 |
|
170 if ( unusedBytes != 0 ) |
|
171 { |
|
172 unusedBytes = blockSize - unusedBytes; |
|
173 neededBlocks += 1; |
|
174 } |
|
175 |
|
176 if ( poolChosen == NULL ) //for the first time this will be true |
|
177 { |
|
178 poolChosen = pool; |
|
179 minUnusedBytes = unusedBytes; |
|
180 minBlocks = neededBlocks; |
|
181 } |
|
182 else |
|
183 { |
|
184 if ( unusedBytes == minUnusedBytes ) |
|
185 { |
|
186 if ( neededBlocks < minBlocks ) |
|
187 { |
|
188 poolChosen = pool; |
|
189 minUnusedBytes = unusedBytes; |
|
190 minBlocks = neededBlocks; |
|
191 } |
|
192 } |
|
193 else if ( unusedBytes < minUnusedBytes ) |
|
194 { |
|
195 poolChosen = pool; |
|
196 minUnusedBytes = unusedBytes; |
|
197 minBlocks = neededBlocks; |
|
198 } |
|
199 } |
|
200 } while ( pool-- != startPool ); |
|
201 |
|
202 if ( poolChosen == NULL ) |
|
203 { |
|
204 __ASSERT_DEBUG ( 0, User::Invariant () ); |
|
205 } |
|
206 |
|
207 TUint allocatedBytes = 0; |
|
208 TBool growthFailed = EFalse; |
|
209 if ( aSize <= poolChosen->FreeSpace () ) |
|
210 { |
|
211 allocatedBytes = poolChosen->Alloc ( aList, aSize ); |
|
212 } |
|
213 else |
|
214 { |
|
215 allocatedBytes = poolChosen->GrowAndAlloc ( aList, aSize, minBlocks ); |
|
216 |
|
217 if ( allocatedBytes == 0 ) |
|
218 { |
|
219 // need to search for the next best match and however, this time |
|
220 // requesting pool to grow may not be worth :), as only error condition is KErrNoMemory |
|
221 growthFailed = ETrue; |
|
222 } |
|
223 } |
|
224 |
|
225 if ( allocatedBytes >= aSize ) |
|
226 { |
|
227 return; |
|
228 } |
|
229 else if ( growthFailed ) |
|
230 { |
|
231 // just get the pool that can satisfy the request, no more best,good,worst match |
|
232 for ( TInt i = 0; i < iChunkPools.Count (); i++ ) |
|
233 { |
|
234 if ( aSize <= iChunkPools[i].FreeSpace () ) |
|
235 { |
|
236 allocatedBytes = iChunkPools[i].Alloc ( aList, aSize ); |
|
237 break; |
|
238 } |
|
239 } |
|
240 } |
|
241 } |
|
242 |
|
243 TInt CChunkManager::BytesAvailable () |
|
244 { |
|
245 TInt freeSpace = iHeapSize - iUsedSpace; |
|
246 |
|
247 freeSpace -= sizeof ( CMemChunk ); |
|
248 |
|
249 return freeSpace <= 0 ? KErrNoMemory : freeSpace; |
|
250 } |
|
251 |
|
252 // return a chain of MBufs to the pool |
|
253 void CChunkManager::Free ( RMemCell* aPtr ) |
|
254 { |
|
255 RChunkPool* pool ( aPtr->Pool () ); |
|
256 pool->Wait (); |
|
257 |
|
258 while ( aPtr != NULL ) |
|
259 { |
|
260 RMemCell* nextPtr = aPtr->Next (); |
|
261 |
|
262 // reset length to the size of the block and break the link |
|
263 aPtr->SetLength ( aPtr->Size () ); |
|
264 aPtr->Link ( NULL ); |
|
265 |
|
266 // adds single block back to its free list and adjusts its free space |
|
267 pool->FreeToPool ( aPtr ); |
|
268 |
|
269 aPtr = nextPtr; |
|
270 } |
|
271 pool->Signal (); |
|
272 } |
|
273 |
|
274 |