1 /* 2 * Copyright 2006-2007, 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 <stdlib.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 status_t 67 ClientMemoryAllocator::InitCheck() 68 { 69 return fLock.InitCheck() < B_OK ? fLock.InitCheck() : B_OK; 70 } 71 72 73 void * 74 ClientMemoryAllocator::Allocate(size_t size, void** _address, bool& newArea) 75 { 76 // Search best matching free block from the list 77 78 block_iterator iterator = fFreeBlocks.GetIterator(); 79 struct block* block; 80 struct block* best = NULL; 81 82 while ((block = iterator.Next()) != NULL) { 83 if (block->size >= size && (best == NULL || block->size < best->size)) 84 best = block; 85 } 86 87 if (best == NULL) { 88 // We didn't find a free block - we need to allocate 89 // another chunk, or resize an existing chunk 90 best = _AllocateChunk(size, newArea); 91 if (best == NULL) 92 return NULL; 93 } else 94 newArea = false; 95 96 // We need to split the chunk into two parts: the one to keep 97 // and the one to give away 98 99 if (best->size == size) { 100 // The simple case: the free block has exactly the size we wanted to have 101 fFreeBlocks.Remove(best); 102 *_address = best->base; 103 return best; 104 } 105 106 // TODO: maybe we should have the user reserve memory in its object 107 // for us, so we don't have to do this here... 108 109 struct block* usedBlock = (struct block*)malloc(sizeof(struct block)); 110 if (usedBlock == NULL) 111 return NULL; 112 113 usedBlock->base = best->base; 114 usedBlock->size = size; 115 usedBlock->chunk = best->chunk; 116 117 best->base += size; 118 best->size -= size; 119 120 *_address = usedBlock->base; 121 return usedBlock; 122 } 123 124 125 void 126 ClientMemoryAllocator::Free(void *cookie) 127 { 128 if (cookie == NULL) 129 return; 130 131 struct block* freeBlock = (struct block*)cookie; 132 133 // search for an adjacent free block 134 135 block_iterator iterator = fFreeBlocks.GetIterator(); 136 struct block* before = NULL; 137 struct block* after = NULL; 138 struct block* block; 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 ((block = iterator.Next()) != NULL) { 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 } else if (before != NULL) { 162 before->size += freeBlock->size; 163 free(freeBlock); 164 } else if (after != NULL) { 165 after->base -= freeBlock->size; 166 after->size += freeBlock->size; 167 free(freeBlock); 168 } else 169 fFreeBlocks.Add(freeBlock); 170 171 // TODO: check if the whole chunk is free now (we could delete it then) 172 } 173 174 175 area_id 176 ClientMemoryAllocator::Area(void* cookie) 177 { 178 struct block* block = (struct block*)cookie; 179 180 if (block != NULL) 181 return block->chunk->area; 182 183 return B_ERROR; 184 } 185 186 187 uint32 188 ClientMemoryAllocator::AreaOffset(void* cookie) 189 { 190 struct block* block = (struct block*)cookie; 191 192 if (block != NULL) 193 return block->base - block->chunk->base; 194 195 return 0; 196 } 197 198 199 bool 200 ClientMemoryAllocator::Lock() 201 { 202 return fLock.ReadLock(); 203 } 204 205 206 void 207 ClientMemoryAllocator::Unlock() 208 { 209 fLock.ReadUnlock(); 210 } 211 212 213 struct block * 214 ClientMemoryAllocator::_AllocateChunk(size_t size, bool& newArea) 215 { 216 // round up to multiple of page size 217 size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); 218 219 // At first, try to resize our existing areas 220 221 chunk_iterator iterator = fChunks.GetIterator(); 222 struct chunk* chunk; 223 while ((chunk = iterator.Next()) != NULL) { 224 status_t status = resize_area(chunk->area, chunk->size + size); 225 if (status == B_OK) { 226 newArea = false; 227 break; 228 } 229 } 230 231 // TODO: resize and relocate while holding the write lock 232 233 struct block* block; 234 uint8* address; 235 236 if (chunk == NULL) { 237 // TODO: temporary measurement as long as resizing areas doesn't 238 // work the way we need (with relocating the area, if needed) 239 if (size < B_PAGE_SIZE * 32) 240 size = B_PAGE_SIZE * 32; 241 242 // create new area for this allocation 243 chunk = (struct chunk*)malloc(sizeof(struct chunk)); 244 if (chunk == NULL) 245 return NULL; 246 247 block = (struct block*)malloc(sizeof(struct block)); 248 if (block == NULL) { 249 free(chunk); 250 return NULL; 251 } 252 253 char name[B_OS_NAME_LENGTH]; 254 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST 255 strcpy(name, "client heap"); 256 #else 257 snprintf(name, sizeof(name), "heap:%ld:%s", fApplication->ClientTeam(), 258 fApplication->SignatureLeaf()); 259 #endif 260 area_id area = create_area(name, (void**)&address, B_ANY_ADDRESS, size, 261 B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); 262 if (area < B_OK) { 263 free(block); 264 free(chunk); 265 return NULL; 266 } 267 268 // add chunk to list 269 270 chunk->area = area; 271 chunk->base = address; 272 chunk->size = size; 273 274 fChunks.Add(chunk); 275 newArea = true; 276 } else { 277 // create new free block for this chunk 278 block = (struct block *)malloc(sizeof(struct block)); 279 if (block == NULL) 280 return NULL; 281 282 address = chunk->base + chunk->size; 283 chunk->size += size; 284 } 285 286 // add block to free list 287 288 block->chunk = chunk; 289 block->base = address; 290 block->size = size; 291 292 fFreeBlocks.Add(block); 293 294 return block; 295 } 296 297