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.Unset(); 331 } 332 } 333 334 335 void* 336 ClientMemory::Allocate(ClientMemoryAllocator* allocator, size_t size, 337 bool& newArea) 338 { 339 fAllocator.SetTo(allocator, false); 340 341 return fAllocator->Allocate(size, &fBlock, newArea); 342 } 343 344 345 area_id 346 ClientMemory::Area() 347 { 348 if (fBlock != NULL) 349 return fBlock->chunk->area; 350 return B_ERROR; 351 } 352 353 354 uint8* 355 ClientMemory::Address() 356 { 357 if (fBlock != NULL) 358 return fBlock->base; 359 return 0; 360 } 361 362 363 uint32 364 ClientMemory::AreaOffset() 365 { 366 if (fBlock != NULL) 367 return fBlock->base - fBlock->chunk->base; 368 return 0; 369 } 370 371 372 // #pragma mark - 373 374 375 ClonedAreaMemory::ClonedAreaMemory() 376 : 377 fClonedArea(-1), 378 fOffset(0), 379 fBase(NULL) 380 { 381 } 382 383 384 ClonedAreaMemory::~ClonedAreaMemory() 385 { 386 if (fClonedArea >= 0) 387 delete_area(fClonedArea); 388 } 389 390 391 void* 392 ClonedAreaMemory::Clone(area_id area, uint32 offset) 393 { 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(); 400 } 401 402 403 area_id 404 ClonedAreaMemory::Area() 405 { 406 return fClonedArea; 407 } 408 409 410 uint8* 411 ClonedAreaMemory::Address() 412 { 413 return fBase + fOffset; 414 } 415 416 417 uint32 418 ClonedAreaMemory::AreaOffset() 419 { 420 return fOffset; 421 } 422