xref: /haiku/src/servers/app/ClientMemoryAllocator.cpp (revision aed35104852941f0f6f3d1dcc5338b5f337d0a3c)
1 /*
2  * Copyright 2006-2012, 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 	BAutolock locker(fLock);
70 
71 	// Search best matching free block from the list
72 
73 	block_iterator iterator = fFreeBlocks.GetIterator();
74 	struct block* block;
75 	struct block* best = NULL;
76 
77 	while ((block = iterator.Next()) != NULL) {
78 		if (block->size >= size && (best == NULL || block->size < best->size))
79 			best = block;
80 	}
81 
82 	if (best == NULL) {
83 		// We didn't find a free block - we need to allocate
84 		// another chunk, or resize an existing chunk
85 		best = _AllocateChunk(size, newArea);
86 		if (best == NULL)
87 			return NULL;
88 	} else
89 		newArea = false;
90 
91 	// We need to split the chunk into two parts: the one to keep
92 	// and the one to give away
93 
94 	if (best->size == size) {
95 		// The simple case: the free block has exactly the size we wanted to have
96 		fFreeBlocks.Remove(best);
97 		*_address = best;
98 		return best->base;
99 	}
100 
101 	// TODO: maybe we should have the user reserve memory in its object
102 	//	for us, so we don't have to do this here...
103 
104 	struct block* usedBlock = (struct block*)malloc(sizeof(struct block));
105 	if (usedBlock == NULL)
106 		return NULL;
107 
108 	usedBlock->base = best->base;
109 	usedBlock->size = size;
110 	usedBlock->chunk = best->chunk;
111 
112 	best->base += size;
113 	best->size -= size;
114 
115 	*_address = usedBlock;
116 	return usedBlock->base;
117 }
118 
119 
120 void
121 ClientMemoryAllocator::Free(block* freeBlock)
122 {
123 	if (freeBlock == NULL)
124 		return;
125 
126 	BAutolock locker(fLock);
127 
128 	// search for an adjacent free block
129 
130 	block_iterator iterator = fFreeBlocks.GetIterator();
131 	struct block* before = NULL;
132 	struct block* after = NULL;
133 	bool inFreeList = true;
134 
135 	if (freeBlock->size != freeBlock->chunk->size) {
136 		// TODO: this could be done better if free blocks are sorted,
137 		//	and if we had one free blocks list per chunk!
138 		//	IOW this is a bit slow...
139 
140 		while (struct block* block = iterator.Next()) {
141 			if (block->chunk != freeBlock->chunk)
142 				continue;
143 
144 			if (block->base + block->size == freeBlock->base)
145 				before = block;
146 
147 			if (block->base == freeBlock->base + freeBlock->size)
148 				after = block;
149 		}
150 
151 		if (before != NULL && after != NULL) {
152 			// merge with adjacent blocks
153 			before->size += after->size + freeBlock->size;
154 			fFreeBlocks.Remove(after);
155 			free(after);
156 			free(freeBlock);
157 			freeBlock = before;
158 		} else if (before != NULL) {
159 			before->size += freeBlock->size;
160 			free(freeBlock);
161 			freeBlock = before;
162 		} else if (after != NULL) {
163 			after->base -= freeBlock->size;
164 			after->size += freeBlock->size;
165 			free(freeBlock);
166 			freeBlock = after;
167 		} else
168 			fFreeBlocks.Add(freeBlock);
169 	} else
170 		inFreeList = false;
171 
172 	if (freeBlock->size == freeBlock->chunk->size) {
173 		// We can delete the chunk now
174 		struct chunk* chunk = freeBlock->chunk;
175 
176 		if (inFreeList)
177 			fFreeBlocks.Remove(freeBlock);
178 		free(freeBlock);
179 
180 		fChunks.Remove(chunk);
181 		delete_area(chunk->area);
182 		fApplication->NotifyDeleteClientArea(chunk->area);
183 
184 		free(chunk);
185 	}
186 }
187 
188 
189 void
190 ClientMemoryAllocator::Dump()
191 {
192 	debug_printf("Application %ld, %s: chunks:\n", fApplication->ClientTeam(),
193 		fApplication->Signature());
194 
195 	chunk_list::Iterator iterator = fChunks.GetIterator();
196 	int32 i = 0;
197 	while (struct chunk* chunk = iterator.Next()) {
198 		debug_printf("  [%4ld] %p, area %ld, base %p, size %lu\n", i++, chunk,
199 			chunk->area, chunk->base, chunk->size);
200 	}
201 
202 	debug_printf("free blocks:\n");
203 
204 	block_list::Iterator blockIterator = fFreeBlocks.GetIterator();
205 	i = 0;
206 	while (struct block* block = blockIterator.Next()) {
207 		debug_printf("  [%6ld] %p, chunk %p, base %p, size %lu\n", i++, block,
208 			block->chunk, block->base, block->size);
209 	}
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 
298 // #pragma mark -
299 
300 
301 ClientMemory::ClientMemory()
302 	:
303 	fBlock(NULL)
304 {
305 }
306 
307 
308 ClientMemory::~ClientMemory()
309 {
310 	if (fBlock != NULL)
311 		fAllocator->Free(fBlock);
312 }
313 
314 
315 void*
316 ClientMemory::Allocate(ClientMemoryAllocator* allocator, size_t size,
317 	bool& newArea)
318 {
319 	fAllocator = allocator;
320 	return fAllocator->Allocate(size, &fBlock, newArea);
321 }
322 
323 
324 area_id
325 ClientMemory::Area()
326 {
327 	if (fBlock != NULL)
328 		return fBlock->chunk->area;
329 	return B_ERROR;
330 }
331 
332 
333 uint8*
334 ClientMemory::Address()
335 {
336 	if (fBlock != NULL)
337 		return fBlock->base;
338 	return 0;
339 }
340 
341 
342 uint32
343 ClientMemory::AreaOffset()
344 {
345 	if (fBlock != NULL)
346 		return fBlock->base - fBlock->chunk->base;
347 	return 0;
348 }
349 
350 
351 // #pragma mark -
352 
353 
354 ClonedAreaMemory::ClonedAreaMemory()
355 	:
356 	fClonedArea(-1),
357 	fOffset(0),
358 	fBase(NULL)
359 {
360 }
361 
362 
363 ClonedAreaMemory::~ClonedAreaMemory()
364 {
365 	if (fClonedArea >= 0)
366 		delete_area(fClonedArea);
367 }
368 
369 
370 void*
371 ClonedAreaMemory::Clone(area_id area, uint32 offset)
372 {
373 	fClonedArea = clone_area("server_memory", (void**)&fBase, B_ANY_ADDRESS,
374 		B_READ_AREA | B_WRITE_AREA, area);
375 	if (fBase == NULL)
376 		return NULL;
377 	fOffset = offset;
378 	return Address();
379 }
380 
381 
382 area_id
383 ClonedAreaMemory::Area()
384 {
385 	return fClonedArea;
386 }
387 
388 
389 uint8*
390 ClonedAreaMemory::Address()
391 {
392 	return fBase + fOffset;
393 }
394 
395 
396 uint32
397 ClonedAreaMemory::AreaOffset()
398 {
399 	return fOffset;
400 }
401