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 <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 struct block* block; 140 141 // TODO: this could be done better if free blocks are sorted, 142 // and if we had one free blocks list per chunk! 143 // IOW this is a bit slow... 144 145 while ((block = iterator.Next()) != NULL) { 146 if (block->chunk != freeBlock->chunk) 147 continue; 148 149 if (block->base + block->size == freeBlock->base) 150 before = block; 151 152 if (block->base == freeBlock->base + freeBlock->size) 153 after = block; 154 } 155 156 if (before != NULL && after != NULL) { 157 // merge with adjacent blocks 158 before->size += after->size + freeBlock->size; 159 fFreeBlocks.Remove(after); 160 free(after); 161 free(freeBlock); 162 } else if (before != NULL) { 163 before->size += freeBlock->size; 164 free(freeBlock); 165 } else if (after != NULL) { 166 after->base -= freeBlock->size; 167 after->size += freeBlock->size; 168 free(freeBlock); 169 } else 170 fFreeBlocks.Add(freeBlock); 171 172 // TODO: check if the whole chunk is free now (we could delete it then) 173 } 174 175 176 area_id 177 ClientMemoryAllocator::Area(void* cookie) 178 { 179 struct block* block = (struct block*)cookie; 180 181 if (block != NULL) 182 return block->chunk->area; 183 184 return B_ERROR; 185 } 186 187 188 uint32 189 ClientMemoryAllocator::AreaOffset(void* cookie) 190 { 191 struct block* block = (struct block*)cookie; 192 193 if (block != NULL) 194 return block->base - block->chunk->base; 195 196 return 0; 197 } 198 199 200 bool 201 ClientMemoryAllocator::Lock() 202 { 203 return fLock.ReadLock(); 204 } 205 206 207 void 208 ClientMemoryAllocator::Unlock() 209 { 210 fLock.ReadUnlock(); 211 } 212 213 214 struct block * 215 ClientMemoryAllocator::_AllocateChunk(size_t size, bool& newArea) 216 { 217 // round up to multiple of page size 218 size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); 219 220 // At first, try to resize our existing areas 221 222 chunk_iterator iterator = fChunks.GetIterator(); 223 struct chunk* chunk; 224 while ((chunk = iterator.Next()) != NULL) { 225 status_t status = resize_area(chunk->area, chunk->size + size); 226 if (status == B_OK) { 227 newArea = false; 228 break; 229 } 230 } 231 232 // TODO: resize and relocate while holding the write lock 233 234 struct block* block; 235 uint8* address; 236 237 if (chunk == NULL) { 238 // TODO: temporary measurement as long as resizing areas doesn't 239 // work the way we need (with relocating the area, if needed) 240 if (size < B_PAGE_SIZE * 32) 241 size = B_PAGE_SIZE * 32; 242 243 // create new area for this allocation 244 chunk = (struct chunk*)malloc(sizeof(struct chunk)); 245 if (chunk == NULL) 246 return NULL; 247 248 block = (struct block*)malloc(sizeof(struct block)); 249 if (block == NULL) { 250 free(chunk); 251 return NULL; 252 } 253 254 char name[B_OS_NAME_LENGTH]; 255 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST 256 strcpy(name, "client heap"); 257 #else 258 snprintf(name, sizeof(name), "heap:%ld:%s", fApplication->ClientTeam(), 259 fApplication->SignatureLeaf()); 260 #endif 261 area_id area = create_area(name, (void**)&address, B_ANY_ADDRESS, size, 262 B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); 263 if (area < B_OK) { 264 free(block); 265 free(chunk); 266 return NULL; 267 } 268 269 // add chunk to list 270 271 chunk->area = area; 272 chunk->base = address; 273 chunk->size = size; 274 275 fChunks.Add(chunk); 276 newArea = true; 277 } else { 278 // create new free block for this chunk 279 block = (struct block *)malloc(sizeof(struct block)); 280 if (block == NULL) 281 return NULL; 282 283 address = chunk->base + chunk->size; 284 chunk->size += size; 285 } 286 287 // add block to free list 288 289 block->chunk = chunk; 290 block->base = address; 291 block->size = size; 292 293 fFreeBlocks.Add(block); 294 295 return block; 296 } 297 298