xref: /haiku/src/servers/app/ClientMemoryAllocator.cpp (revision e5d65858f2361fe0552495b61620c84dcee6bc00)
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);
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 	fBlock(NULL)
320 {
321 }
322 
323 
324 ClientMemory::~ClientMemory()
325 {
326 	if (fBlock != NULL)
327 		fAllocator->Free(fBlock);
328 }
329 
330 
331 void*
332 ClientMemory::Allocate(ClientMemoryAllocator* allocator, size_t size,
333 	bool& newArea)
334 {
335 	fAllocator = allocator;
336 	return fAllocator->Allocate(size, &fBlock, newArea);
337 }
338 
339 
340 area_id
341 ClientMemory::Area()
342 {
343 	if (fBlock != NULL)
344 		return fBlock->chunk->area;
345 	return B_ERROR;
346 }
347 
348 
349 uint8*
350 ClientMemory::Address()
351 {
352 	if (fBlock != NULL)
353 		return fBlock->base;
354 	return 0;
355 }
356 
357 
358 uint32
359 ClientMemory::AreaOffset()
360 {
361 	if (fBlock != NULL)
362 		return fBlock->base - fBlock->chunk->base;
363 	return 0;
364 }
365 
366 
367 // #pragma mark -
368 
369 
370 ClonedAreaMemory::ClonedAreaMemory()
371 	:
372 	fClonedArea(-1),
373 	fOffset(0),
374 	fBase(NULL)
375 {
376 }
377 
378 
379 ClonedAreaMemory::~ClonedAreaMemory()
380 {
381 	if (fClonedArea >= 0)
382 		delete_area(fClonedArea);
383 }
384 
385 
386 void*
387 ClonedAreaMemory::Clone(area_id area, uint32 offset)
388 {
389 	fClonedArea = clone_area("server_memory", (void**)&fBase, B_ANY_ADDRESS,
390 		B_READ_AREA | B_WRITE_AREA, area);
391 	if (fBase == NULL)
392 		return NULL;
393 	fOffset = offset;
394 	return Address();
395 }
396 
397 
398 area_id
399 ClonedAreaMemory::Area()
400 {
401 	return fClonedArea;
402 }
403 
404 
405 uint8*
406 ClonedAreaMemory::Address()
407 {
408 	return fBase + fOffset;
409 }
410 
411 
412 uint32
413 ClonedAreaMemory::AreaOffset()
414 {
415 	return fOffset;
416 }
417