1 /* 2 * Copyright 2006-2010, 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 Note, this class doesn't provide any real locking - you need to have the 15 ServerApp locked when interacting with any method of this class. 16 17 The Lock()/Unlock() methods are needed whenever you access a pointer that 18 lies within an area allocated using this class. This is needed because an 19 area might be temporarily unavailable or might be relocated at any time. 20 */ 21 22 // TODO: right now, areas will always stay static until they are deleted; 23 // locking is not yet done or enforced! 24 25 #include "ClientMemoryAllocator.h" 26 #include "ServerApp.h" 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 31 32 typedef block_list::Iterator block_iterator; 33 typedef chunk_list::Iterator chunk_iterator; 34 35 36 ClientMemoryAllocator::ClientMemoryAllocator(ServerApp* application) 37 : 38 fApplication(application) 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 // Search best matching free block from the list 70 71 block_iterator iterator = fFreeBlocks.GetIterator(); 72 struct block* block; 73 struct block* best = NULL; 74 75 while ((block = iterator.Next()) != NULL) { 76 if (block->size >= size && (best == NULL || block->size < best->size)) 77 best = block; 78 } 79 80 if (best == NULL) { 81 // We didn't find a free block - we need to allocate 82 // another chunk, or resize an existing chunk 83 best = _AllocateChunk(size, newArea); 84 if (best == NULL) 85 return NULL; 86 } else 87 newArea = false; 88 89 // We need to split the chunk into two parts: the one to keep 90 // and the one to give away 91 92 if (best->size == size) { 93 // The simple case: the free block has exactly the size we wanted to have 94 fFreeBlocks.Remove(best); 95 *_address = best; 96 return best->base; 97 } 98 99 // TODO: maybe we should have the user reserve memory in its object 100 // for us, so we don't have to do this here... 101 102 struct block* usedBlock = (struct block*)malloc(sizeof(struct block)); 103 if (usedBlock == NULL) 104 return NULL; 105 106 usedBlock->base = best->base; 107 usedBlock->size = size; 108 usedBlock->chunk = best->chunk; 109 110 best->base += size; 111 best->size -= size; 112 113 *_address = usedBlock; 114 return usedBlock->base; 115 } 116 117 118 void 119 ClientMemoryAllocator::Free(block* freeBlock) 120 { 121 if (freeBlock == NULL) 122 return; 123 124 // search for an adjacent free block 125 126 block_iterator iterator = fFreeBlocks.GetIterator(); 127 struct block* before = NULL; 128 struct block* after = NULL; 129 130 // TODO: this could be done better if free blocks are sorted, 131 // and if we had one free blocks list per chunk! 132 // IOW this is a bit slow... 133 134 while (struct block* block = iterator.Next()) { 135 if (block->chunk != freeBlock->chunk) 136 continue; 137 138 if (block->base + block->size == freeBlock->base) 139 before = block; 140 141 if (block->base == freeBlock->base + freeBlock->size) 142 after = block; 143 } 144 145 if (before != NULL && after != NULL) { 146 // merge with adjacent blocks 147 before->size += after->size + freeBlock->size; 148 fFreeBlocks.Remove(after); 149 free(after); 150 free(freeBlock); 151 freeBlock = before; 152 } else if (before != NULL) { 153 before->size += freeBlock->size; 154 free(freeBlock); 155 freeBlock = before; 156 } else if (after != NULL) { 157 after->base -= freeBlock->size; 158 after->size += freeBlock->size; 159 free(freeBlock); 160 freeBlock = after; 161 } else 162 fFreeBlocks.Add(freeBlock); 163 164 if (freeBlock->size == freeBlock->chunk->size) { 165 // We can delete the chunk now 166 struct chunk* chunk = freeBlock->chunk; 167 168 fFreeBlocks.Remove(freeBlock); 169 free(freeBlock); 170 171 fChunks.Remove(chunk); 172 delete_area(chunk->area); 173 free(chunk); 174 } 175 } 176 177 178 void 179 ClientMemoryAllocator::Dump() 180 { 181 debug_printf("Application %ld, %s: chunks:\n", fApplication->ClientTeam(), 182 fApplication->Signature()); 183 184 chunk_list::Iterator iterator = fChunks.GetIterator(); 185 int32 i = 0; 186 while (struct chunk* chunk = iterator.Next()) { 187 debug_printf(" [%4ld] %p, area %ld, base %p, size %lu\n", i++, chunk, 188 chunk->area, chunk->base, chunk->size); 189 } 190 191 debug_printf("free blocks:\n"); 192 193 block_list::Iterator blockIterator = fFreeBlocks.GetIterator(); 194 i = 0; 195 while (struct block* block = blockIterator.Next()) { 196 debug_printf(" [%6ld] %p, chunk %p, base %p, size %lu\n", i++, block, 197 block->chunk, block->base, block->size); 198 } 199 } 200 201 202 struct block* 203 ClientMemoryAllocator::_AllocateChunk(size_t size, bool& newArea) 204 { 205 // round up to multiple of page size 206 size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); 207 208 // At first, try to resize our existing areas 209 210 chunk_iterator iterator = fChunks.GetIterator(); 211 struct chunk* chunk; 212 while ((chunk = iterator.Next()) != NULL) { 213 status_t status = resize_area(chunk->area, chunk->size + size); 214 if (status == B_OK) { 215 newArea = false; 216 break; 217 } 218 } 219 220 // TODO: resize and relocate while holding the write lock 221 222 struct block* block; 223 uint8* address; 224 225 if (chunk == NULL) { 226 // TODO: temporary measurement as long as resizing areas doesn't 227 // work the way we need (with relocating the area, if needed) 228 if (size < B_PAGE_SIZE * 32) 229 size = B_PAGE_SIZE * 32; 230 231 // create new area for this allocation 232 chunk = (struct chunk*)malloc(sizeof(struct chunk)); 233 if (chunk == NULL) 234 return NULL; 235 236 block = (struct block*)malloc(sizeof(struct block)); 237 if (block == NULL) { 238 free(chunk); 239 return NULL; 240 } 241 242 char name[B_OS_NAME_LENGTH]; 243 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST 244 strcpy(name, "client heap"); 245 #else 246 snprintf(name, sizeof(name), "heap:%ld:%s", fApplication->ClientTeam(), 247 fApplication->SignatureLeaf()); 248 #endif 249 area_id area = create_area(name, (void**)&address, B_ANY_ADDRESS, size, 250 B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); 251 if (area < B_OK) { 252 free(block); 253 free(chunk); 254 return NULL; 255 } 256 257 // add chunk to list 258 259 chunk->area = area; 260 chunk->base = address; 261 chunk->size = size; 262 263 fChunks.Add(chunk); 264 newArea = true; 265 } else { 266 // create new free block for this chunk 267 block = (struct block *)malloc(sizeof(struct block)); 268 if (block == NULL) 269 return NULL; 270 271 address = chunk->base + chunk->size; 272 chunk->size += size; 273 } 274 275 // add block to free list 276 277 block->chunk = chunk; 278 block->base = address; 279 block->size = size; 280 281 fFreeBlocks.Add(block); 282 283 return block; 284 } 285 286 287 ClientMemory::ClientMemory() 288 : 289 fBlock(NULL) 290 { 291 } 292 293 294 ClientMemory::~ClientMemory() 295 { 296 if (fBlock != NULL) 297 fAllocator->Free(fBlock); 298 } 299 300 301 void* 302 ClientMemory::Allocate(ClientMemoryAllocator* allocator, size_t size, 303 bool& newArea) 304 { 305 fAllocator = allocator; 306 return fAllocator->Allocate(size, &fBlock, newArea); 307 } 308 309 310 area_id 311 ClientMemory::Area() 312 { 313 if (fBlock != NULL) 314 return fBlock->chunk->area; 315 return B_ERROR; 316 } 317 318 319 uint8* 320 ClientMemory::Address() 321 { 322 if (fBlock != NULL) 323 return fBlock->base; 324 return 0; 325 } 326 327 328 uint32 329 ClientMemory::AreaOffset() 330 { 331 if (fBlock != NULL) 332 return fBlock->base - fBlock->chunk->base; 333 return 0; 334 } 335 336 337 ClonedAreaMemory::ClonedAreaMemory() 338 : 339 fClonedArea(-1), 340 fOffset(0), 341 fBase(NULL) 342 { 343 } 344 345 346 ClonedAreaMemory::~ClonedAreaMemory() 347 { 348 if (fClonedArea >= 0) 349 delete_area(fClonedArea); 350 } 351 352 353 void* 354 ClonedAreaMemory::Clone(area_id area, uint32 offset) 355 { 356 fClonedArea = clone_area("server_memory", (void**)&fBase, B_ANY_ADDRESS, 357 B_READ_AREA | B_WRITE_AREA, area); 358 if (fBase == NULL) 359 return NULL; 360 fOffset = offset; 361 return Address(); 362 } 363 364 365 area_id 366 ClonedAreaMemory::Area() 367 { 368 return fClonedArea; 369 } 370 371 372 uint8* 373 ClonedAreaMemory::Address() 374 { 375 return fBase + fOffset; 376 } 377 378 379 uint32 380 ClonedAreaMemory::AreaOffset() 381 { 382 return fOffset; 383 } 384