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