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 fLock("client memory lock") 40 { 41 } 42 43 44 ClientMemoryAllocator::~ClientMemoryAllocator() 45 { 46 // delete all areas and chunks/blocks that are still allocated 47 48 while (true) { 49 struct block* block = fFreeBlocks.RemoveHead(); 50 if (block == NULL) 51 break; 52 53 free(block); 54 } 55 56 while (true) { 57 struct chunk* chunk = fChunks.RemoveHead(); 58 if (chunk == NULL) 59 break; 60 61 delete_area(chunk->area); 62 free(chunk); 63 } 64 } 65 66 67 status_t 68 ClientMemoryAllocator::InitCheck() 69 { 70 return fLock.InitCheck() < B_OK ? fLock.InitCheck() : B_OK; 71 } 72 73 74 void* 75 ClientMemoryAllocator::Allocate(size_t size, void** _address, bool& newArea) 76 { 77 // Search best matching free block from the list 78 79 block_iterator iterator = fFreeBlocks.GetIterator(); 80 struct block* block; 81 struct block* best = NULL; 82 83 while ((block = iterator.Next()) != NULL) { 84 if (block->size >= size && (best == NULL || block->size < best->size)) 85 best = block; 86 } 87 88 if (best == NULL) { 89 // We didn't find a free block - we need to allocate 90 // another chunk, or resize an existing chunk 91 best = _AllocateChunk(size, newArea); 92 if (best == NULL) 93 return NULL; 94 } else 95 newArea = false; 96 97 // We need to split the chunk into two parts: the one to keep 98 // and the one to give away 99 100 if (best->size == size) { 101 // The simple case: the free block has exactly the size we wanted to have 102 fFreeBlocks.Remove(best); 103 *_address = best->base; 104 return best; 105 } 106 107 // TODO: maybe we should have the user reserve memory in its object 108 // for us, so we don't have to do this here... 109 110 struct block* usedBlock = (struct block*)malloc(sizeof(struct block)); 111 if (usedBlock == NULL) 112 return NULL; 113 114 usedBlock->base = best->base; 115 usedBlock->size = size; 116 usedBlock->chunk = best->chunk; 117 118 best->base += size; 119 best->size -= size; 120 121 *_address = usedBlock->base; 122 return usedBlock; 123 } 124 125 126 void 127 ClientMemoryAllocator::Free(void* cookie) 128 { 129 if (cookie == NULL) 130 return; 131 132 struct block* freeBlock = (struct block*)cookie; 133 134 // search for an adjacent free block 135 136 block_iterator iterator = fFreeBlocks.GetIterator(); 137 struct block* before = NULL; 138 struct block* after = NULL; 139 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 174 if (freeBlock->size == freeBlock->chunk->size) { 175 // We can delete the chunk now 176 struct chunk* chunk = freeBlock->chunk; 177 178 fFreeBlocks.Remove(freeBlock); 179 free(freeBlock); 180 181 fChunks.Remove(chunk); 182 delete_area(chunk->area); 183 free(chunk); 184 } 185 } 186 187 188 area_id 189 ClientMemoryAllocator::Area(void* cookie) 190 { 191 struct block* block = (struct block*)cookie; 192 193 if (block != NULL) 194 return block->chunk->area; 195 196 return B_ERROR; 197 } 198 199 200 uint32 201 ClientMemoryAllocator::AreaOffset(void* cookie) 202 { 203 struct block* block = (struct block*)cookie; 204 205 if (block != NULL) 206 return block->base - block->chunk->base; 207 208 return 0; 209 } 210 211 212 bool 213 ClientMemoryAllocator::Lock() 214 { 215 return fLock.ReadLock(); 216 } 217 218 219 void 220 ClientMemoryAllocator::Unlock() 221 { 222 fLock.ReadUnlock(); 223 } 224 225 226 void 227 ClientMemoryAllocator::Dump() 228 { 229 AutoReadLocker locker(fLock); 230 231 debug_printf("Application %ld, %s: chunks:\n", fApplication->ClientTeam(), 232 fApplication->Signature()); 233 234 chunk_list::Iterator iterator = fChunks.GetIterator(); 235 int32 i = 0; 236 while (struct chunk* chunk = iterator.Next()) { 237 debug_printf(" [%4ld] %p, area %ld, base %p, size %lu\n", i++, chunk, 238 chunk->area, chunk->base, chunk->size); 239 } 240 241 debug_printf("free blocks:\n"); 242 243 block_list::Iterator blockIterator = fFreeBlocks.GetIterator(); 244 i = 0; 245 while (struct block* block = blockIterator.Next()) { 246 debug_printf(" [%6ld] %p, chunk %p, base %p, size %lu\n", i++, block, 247 block->chunk, block->base, block->size); 248 } 249 } 250 251 252 struct block* 253 ClientMemoryAllocator::_AllocateChunk(size_t size, bool& newArea) 254 { 255 // round up to multiple of page size 256 size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); 257 258 // At first, try to resize our existing areas 259 260 chunk_iterator iterator = fChunks.GetIterator(); 261 struct chunk* chunk; 262 while ((chunk = iterator.Next()) != NULL) { 263 status_t status = resize_area(chunk->area, chunk->size + size); 264 if (status == B_OK) { 265 newArea = false; 266 break; 267 } 268 } 269 270 // TODO: resize and relocate while holding the write lock 271 272 struct block* block; 273 uint8* address; 274 275 if (chunk == NULL) { 276 // TODO: temporary measurement as long as resizing areas doesn't 277 // work the way we need (with relocating the area, if needed) 278 if (size < B_PAGE_SIZE * 32) 279 size = B_PAGE_SIZE * 32; 280 281 // create new area for this allocation 282 chunk = (struct chunk*)malloc(sizeof(struct chunk)); 283 if (chunk == NULL) 284 return NULL; 285 286 block = (struct block*)malloc(sizeof(struct block)); 287 if (block == NULL) { 288 free(chunk); 289 return NULL; 290 } 291 292 char name[B_OS_NAME_LENGTH]; 293 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST 294 strcpy(name, "client heap"); 295 #else 296 snprintf(name, sizeof(name), "heap:%ld:%s", fApplication->ClientTeam(), 297 fApplication->SignatureLeaf()); 298 #endif 299 area_id area = create_area(name, (void**)&address, B_ANY_ADDRESS, size, 300 B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); 301 if (area < B_OK) { 302 free(block); 303 free(chunk); 304 return NULL; 305 } 306 307 // add chunk to list 308 309 chunk->area = area; 310 chunk->base = address; 311 chunk->size = size; 312 313 fChunks.Add(chunk); 314 newArea = true; 315 } else { 316 // create new free block for this chunk 317 block = (struct block *)malloc(sizeof(struct block)); 318 if (block == NULL) 319 return NULL; 320 321 address = chunk->base + chunk->size; 322 chunk->size += size; 323 } 324 325 // add block to free list 326 327 block->chunk = chunk; 328 block->base = address; 329 block->size = size; 330 331 fFreeBlocks.Add(block); 332 333 return block; 334 } 335 336