1 /* 2 * Copyright 2006-2013, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 */ 8 9 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. 13 */ 14 15 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. 19 20 21 #include "ClientMemoryAllocator.h" 22 23 #include <stdio.h> 24 #include <stdlib.h> 25 26 #include <Autolock.h> 27 28 #include "ServerApp.h" 29 30 31 typedef block_list::Iterator block_iterator; 32 typedef chunk_list::Iterator chunk_iterator; 33 34 35 ClientMemoryAllocator::ClientMemoryAllocator(ServerApp* application) 36 : 37 fApplication(application), 38 fLock("client memory lock") 39 { 40 } 41 42 43 ClientMemoryAllocator::~ClientMemoryAllocator() 44 { 45 // delete all areas and chunks/blocks that are still allocated 46 47 while (true) { 48 struct block* block = fFreeBlocks.RemoveHead(); 49 if (block == NULL) 50 break; 51 52 free(block); 53 } 54 55 while (true) { 56 struct chunk* chunk = fChunks.RemoveHead(); 57 if (chunk == NULL) 58 break; 59 60 delete_area(chunk->area); 61 free(chunk); 62 } 63 } 64 65 66 void* 67 ClientMemoryAllocator::Allocate(size_t size, block** _address, bool& newArea) 68 { 69 // A detached allocator no longer allows any further allocations 70 if (fApplication == NULL) 71 return NULL; 72 73 BAutolock locker(fLock); 74 75 // Search best matching free block from the list 76 77 block_iterator iterator = fFreeBlocks.GetIterator(); 78 struct block* block; 79 struct block* best = NULL; 80 81 while ((block = iterator.Next()) != NULL) { 82 if (block->size >= size && (best == NULL || block->size < best->size)) 83 best = block; 84 } 85 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; 94 95 // We need to split the chunk into two parts: the one to keep 96 // and the one to give away 97 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; 103 } 104 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... 107 108 struct block* usedBlock = (struct block*)malloc(sizeof(struct block)); 109 if (usedBlock == NULL) 110 return NULL; 111 112 usedBlock->base = best->base; 113 usedBlock->size = size; 114 usedBlock->chunk = best->chunk; 115 116 best->base += size; 117 best->size -= size; 118 119 *_address = usedBlock; 120 return usedBlock->base; 121 } 122 123 124 void 125 ClientMemoryAllocator::Free(block* freeBlock) 126 { 127 if (freeBlock == NULL) 128 return; 129 130 BAutolock locker(fLock); 131 132 // search for an adjacent free block 133 134 block_iterator iterator = fFreeBlocks.GetIterator(); 135 struct block* before = NULL; 136 struct block* after = NULL; 137 bool inFreeList = true; 138 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... 143 144 while (struct block* block = iterator.Next()) { 145 if (block->chunk != freeBlock->chunk) 146 continue; 147 148 if (block->base + block->size == freeBlock->base) 149 before = block; 150 151 if (block->base == freeBlock->base + freeBlock->size) 152 after = block; 153 } 154 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; 175 176 if (freeBlock->size == freeBlock->chunk->size) { 177 // We can delete the chunk now 178 struct chunk* chunk = freeBlock->chunk; 179 180 if (inFreeList) 181 fFreeBlocks.Remove(freeBlock); 182 free(freeBlock); 183 184 fChunks.Remove(chunk); 185 delete_area(chunk->area); 186 187 if (fApplication != NULL) 188 fApplication->NotifyDeleteClientArea(chunk->area); 189 190 free(chunk); 191 } 192 } 193 194 195 void 196 ClientMemoryAllocator::Detach() 197 { 198 BAutolock locker(fLock); 199 fApplication = NULL; 200 } 201 202 203 void 204 ClientMemoryAllocator::Dump() 205 { 206 if (fApplication != NULL) { 207 debug_printf("Application %" B_PRId32 ", %s: chunks:\n", 208 fApplication->ClientTeam(), fApplication->Signature()); 209 } 210 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); 216 } 217 218 debug_printf("free blocks:\n"); 219 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); 225 } 226 } 227 228 229 struct block* 230 ClientMemoryAllocator::_AllocateChunk(size_t size, bool& newArea) 231 { 232 // round up to multiple of page size 233 size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); 234 235 // At first, try to resize our existing areas 236 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; 244 } 245 } 246 247 // TODO: resize and relocate while holding the write lock 248 249 struct block* block; 250 uint8* address; 251 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; 257 258 // create new area for this allocation 259 chunk = (struct chunk*)malloc(sizeof(struct chunk)); 260 if (chunk == NULL) 261 return NULL; 262 263 block = (struct block*)malloc(sizeof(struct block)); 264 if (block == NULL) { 265 free(chunk); 266 return NULL; 267 } 268 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 | B_CLONEABLE_AREA); 278 if (area < B_OK) { 279 free(block); 280 free(chunk); 281 return NULL; 282 } 283 284 // add chunk to list 285 286 chunk->area = area; 287 chunk->base = address; 288 chunk->size = size; 289 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; 297 298 address = chunk->base + chunk->size; 299 chunk->size += size; 300 } 301 302 // add block to free list 303 304 block->chunk = chunk; 305 block->base = address; 306 block->size = size; 307 308 fFreeBlocks.Add(block); 309 310 return block; 311 } 312 313 314 // #pragma mark - 315 316 317 ClientMemory::ClientMemory() 318 : 319 fAllocator(NULL), 320 fBlock(NULL) 321 { 322 } 323 324 325 ClientMemory::~ClientMemory() 326 { 327 if (fAllocator != NULL) { 328 if (fBlock != NULL) 329 fAllocator->Free(fBlock); 330 fAllocator->ReleaseReference(); 331 } 332 } 333 334 335 void* 336 ClientMemory::Allocate(ClientMemoryAllocator* allocator, size_t size, 337 bool& newArea) 338 { 339 fAllocator = allocator; 340 fAllocator->AcquireReference(); 341 342 return fAllocator->Allocate(size, &fBlock, newArea); 343 } 344 345 346 area_id 347 ClientMemory::Area() 348 { 349 if (fBlock != NULL) 350 return fBlock->chunk->area; 351 return B_ERROR; 352 } 353 354 355 uint8* 356 ClientMemory::Address() 357 { 358 if (fBlock != NULL) 359 return fBlock->base; 360 return 0; 361 } 362 363 364 uint32 365 ClientMemory::AreaOffset() 366 { 367 if (fBlock != NULL) 368 return fBlock->base - fBlock->chunk->base; 369 return 0; 370 } 371 372 373 // #pragma mark - 374 375 376 ClonedAreaMemory::ClonedAreaMemory() 377 : 378 fClonedArea(-1), 379 fOffset(0), 380 fBase(NULL) 381 { 382 } 383 384 385 ClonedAreaMemory::~ClonedAreaMemory() 386 { 387 if (fClonedArea >= 0) 388 delete_area(fClonedArea); 389 } 390 391 392 void* 393 ClonedAreaMemory::Clone(area_id area, uint32 offset) 394 { 395 fClonedArea = clone_area("server_memory", (void**)&fBase, B_ANY_ADDRESS, 396 B_READ_AREA | B_WRITE_AREA, area); 397 if (fBase == NULL) 398 return NULL; 399 fOffset = offset; 400 return Address(); 401 } 402 403 404 area_id 405 ClonedAreaMemory::Area() 406 { 407 return fClonedArea; 408 } 409 410 411 uint8* 412 ClonedAreaMemory::Address() 413 { 414 return fBase + fOffset; 415 } 416 417 418 uint32 419 ClonedAreaMemory::AreaOffset() 420 { 421 return fOffset; 422 } 423