btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / servers / app / ClientMemoryAllocator.cpp
blob0693ce8f85fb272a7ffcb30d76fb2f4043d69d9d
1 /*
2 * Copyright 2006-2013, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 */
10 /*! This class manages a pool of areas for one client. The client is supposed
11 to clone these areas into its own address space to access the data.
12 This mechanism is only used for bitmaps for far.
16 // TODO: areas could be relocated if needed (to be able to resize them)
17 // However, this would require a lock whenever a block of memory
18 // allocated by this allocator is accessed.
21 #include "ClientMemoryAllocator.h"
23 #include <stdio.h>
24 #include <stdlib.h>
26 #include <Autolock.h>
28 #include "ServerApp.h"
31 typedef block_list::Iterator block_iterator;
32 typedef chunk_list::Iterator chunk_iterator;
35 ClientMemoryAllocator::ClientMemoryAllocator(ServerApp* application)
37 fApplication(application),
38 fLock("client memory lock")
43 ClientMemoryAllocator::~ClientMemoryAllocator()
45 // delete all areas and chunks/blocks that are still allocated
47 while (true) {
48 struct block* block = fFreeBlocks.RemoveHead();
49 if (block == NULL)
50 break;
52 free(block);
55 while (true) {
56 struct chunk* chunk = fChunks.RemoveHead();
57 if (chunk == NULL)
58 break;
60 delete_area(chunk->area);
61 free(chunk);
66 void*
67 ClientMemoryAllocator::Allocate(size_t size, block** _address, bool& newArea)
69 // A detached allocator no longer allows any further allocations
70 if (fApplication == NULL)
71 return NULL;
73 BAutolock locker(fLock);
75 // Search best matching free block from the list
77 block_iterator iterator = fFreeBlocks.GetIterator();
78 struct block* block;
79 struct block* best = NULL;
81 while ((block = iterator.Next()) != NULL) {
82 if (block->size >= size && (best == NULL || block->size < best->size))
83 best = block;
86 if (best == NULL) {
87 // We didn't find a free block - we need to allocate
88 // another chunk, or resize an existing chunk
89 best = _AllocateChunk(size, newArea);
90 if (best == NULL)
91 return NULL;
92 } else
93 newArea = false;
95 // We need to split the chunk into two parts: the one to keep
96 // and the one to give away
98 if (best->size == size) {
99 // The simple case: the free block has exactly the size we wanted to have
100 fFreeBlocks.Remove(best);
101 *_address = best;
102 return best->base;
105 // TODO: maybe we should have the user reserve memory in its object
106 // for us, so we don't have to do this here...
108 struct block* usedBlock = (struct block*)malloc(sizeof(struct block));
109 if (usedBlock == NULL)
110 return NULL;
112 usedBlock->base = best->base;
113 usedBlock->size = size;
114 usedBlock->chunk = best->chunk;
116 best->base += size;
117 best->size -= size;
119 *_address = usedBlock;
120 return usedBlock->base;
124 void
125 ClientMemoryAllocator::Free(block* freeBlock)
127 if (freeBlock == NULL)
128 return;
130 BAutolock locker(fLock);
132 // search for an adjacent free block
134 block_iterator iterator = fFreeBlocks.GetIterator();
135 struct block* before = NULL;
136 struct block* after = NULL;
137 bool inFreeList = true;
139 if (freeBlock->size != freeBlock->chunk->size) {
140 // TODO: this could be done better if free blocks are sorted,
141 // and if we had one free blocks list per chunk!
142 // IOW this is a bit slow...
144 while (struct block* block = iterator.Next()) {
145 if (block->chunk != freeBlock->chunk)
146 continue;
148 if (block->base + block->size == freeBlock->base)
149 before = block;
151 if (block->base == freeBlock->base + freeBlock->size)
152 after = block;
155 if (before != NULL && after != NULL) {
156 // merge with adjacent blocks
157 before->size += after->size + freeBlock->size;
158 fFreeBlocks.Remove(after);
159 free(after);
160 free(freeBlock);
161 freeBlock = before;
162 } else if (before != NULL) {
163 before->size += freeBlock->size;
164 free(freeBlock);
165 freeBlock = before;
166 } else if (after != NULL) {
167 after->base -= freeBlock->size;
168 after->size += freeBlock->size;
169 free(freeBlock);
170 freeBlock = after;
171 } else
172 fFreeBlocks.Add(freeBlock);
173 } else
174 inFreeList = false;
176 if (freeBlock->size == freeBlock->chunk->size) {
177 // We can delete the chunk now
178 struct chunk* chunk = freeBlock->chunk;
180 if (inFreeList)
181 fFreeBlocks.Remove(freeBlock);
182 free(freeBlock);
184 fChunks.Remove(chunk);
185 delete_area(chunk->area);
187 if (fApplication != NULL)
188 fApplication->NotifyDeleteClientArea(chunk->area);
190 free(chunk);
195 void
196 ClientMemoryAllocator::Detach()
198 BAutolock locker(fLock);
199 fApplication = NULL;
203 void
204 ClientMemoryAllocator::Dump()
206 if (fApplication != NULL) {
207 debug_printf("Application %" B_PRId32 ", %s: chunks:\n",
208 fApplication->ClientTeam(), fApplication->Signature());
211 chunk_list::Iterator iterator = fChunks.GetIterator();
212 int32 i = 0;
213 while (struct chunk* chunk = iterator.Next()) {
214 debug_printf(" [%4" B_PRId32 "] %p, area %" B_PRId32 ", base %p, "
215 "size %lu\n", i++, chunk, chunk->area, chunk->base, chunk->size);
218 debug_printf("free blocks:\n");
220 block_list::Iterator blockIterator = fFreeBlocks.GetIterator();
221 i = 0;
222 while (struct block* block = blockIterator.Next()) {
223 debug_printf(" [%6" B_PRId32 "] %p, chunk %p, base %p, size %lu\n",
224 i++, block, block->chunk, block->base, block->size);
229 struct block*
230 ClientMemoryAllocator::_AllocateChunk(size_t size, bool& newArea)
232 // round up to multiple of page size
233 size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
235 // At first, try to resize our existing areas
237 chunk_iterator iterator = fChunks.GetIterator();
238 struct chunk* chunk;
239 while ((chunk = iterator.Next()) != NULL) {
240 status_t status = resize_area(chunk->area, chunk->size + size);
241 if (status == B_OK) {
242 newArea = false;
243 break;
247 // TODO: resize and relocate while holding the write lock
249 struct block* block;
250 uint8* address;
252 if (chunk == NULL) {
253 // TODO: temporary measurement as long as resizing areas doesn't
254 // work the way we need (with relocating the area, if needed)
255 if (size < B_PAGE_SIZE * 32)
256 size = B_PAGE_SIZE * 32;
258 // create new area for this allocation
259 chunk = (struct chunk*)malloc(sizeof(struct chunk));
260 if (chunk == NULL)
261 return NULL;
263 block = (struct block*)malloc(sizeof(struct block));
264 if (block == NULL) {
265 free(chunk);
266 return NULL;
269 char name[B_OS_NAME_LENGTH];
270 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
271 strcpy(name, "client heap");
272 #else
273 snprintf(name, sizeof(name), "heap:%" B_PRId32 ":%s",
274 fApplication->ClientTeam(), fApplication->SignatureLeaf());
275 #endif
276 area_id area = create_area(name, (void**)&address, B_ANY_ADDRESS, size,
277 B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
278 if (area < B_OK) {
279 free(block);
280 free(chunk);
281 return NULL;
284 // add chunk to list
286 chunk->area = area;
287 chunk->base = address;
288 chunk->size = size;
290 fChunks.Add(chunk);
291 newArea = true;
292 } else {
293 // create new free block for this chunk
294 block = (struct block *)malloc(sizeof(struct block));
295 if (block == NULL)
296 return NULL;
298 address = chunk->base + chunk->size;
299 chunk->size += size;
302 // add block to free list
304 block->chunk = chunk;
305 block->base = address;
306 block->size = size;
308 fFreeBlocks.Add(block);
310 return block;
314 // #pragma mark -
317 ClientMemory::ClientMemory()
319 fAllocator(NULL),
320 fBlock(NULL)
325 ClientMemory::~ClientMemory()
327 if (fBlock != NULL)
328 fAllocator->Free(fBlock);
329 if (fAllocator != NULL)
330 fAllocator->ReleaseReference();
334 void*
335 ClientMemory::Allocate(ClientMemoryAllocator* allocator, size_t size,
336 bool& newArea)
338 fAllocator = allocator;
339 fAllocator->AcquireReference();
341 return fAllocator->Allocate(size, &fBlock, newArea);
345 area_id
346 ClientMemory::Area()
348 if (fBlock != NULL)
349 return fBlock->chunk->area;
350 return B_ERROR;
354 uint8*
355 ClientMemory::Address()
357 if (fBlock != NULL)
358 return fBlock->base;
359 return 0;
363 uint32
364 ClientMemory::AreaOffset()
366 if (fBlock != NULL)
367 return fBlock->base - fBlock->chunk->base;
368 return 0;
372 // #pragma mark -
375 ClonedAreaMemory::ClonedAreaMemory()
377 fClonedArea(-1),
378 fOffset(0),
379 fBase(NULL)
384 ClonedAreaMemory::~ClonedAreaMemory()
386 if (fClonedArea >= 0)
387 delete_area(fClonedArea);
391 void*
392 ClonedAreaMemory::Clone(area_id area, uint32 offset)
394 fClonedArea = clone_area("server_memory", (void**)&fBase, B_ANY_ADDRESS,
395 B_READ_AREA | B_WRITE_AREA, area);
396 if (fBase == NULL)
397 return NULL;
398 fOffset = offset;
399 return Address();
403 area_id
404 ClonedAreaMemory::Area()
406 return fClonedArea;
410 uint8*
411 ClonedAreaMemory::Address()
413 return fBase + fOffset;
417 uint32
418 ClonedAreaMemory::AreaOffset()
420 return fOffset;