xref: /haiku/src/servers/app/ClientMemoryAllocator.cpp (revision 4b3408bdf6d8e1d95c86c45123bad4b2c45807ac)
1 /*
2  * Copyright 2006-2010, 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 {
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 	// Search best matching free block from the list
70 
71 	block_iterator iterator = fFreeBlocks.GetIterator();
72 	struct block* block;
73 	struct block* best = NULL;
74 
75 	while ((block = iterator.Next()) != NULL) {
76 		if (block->size >= size && (best == NULL || block->size < best->size))
77 			best = block;
78 	}
79 
80 	if (best == NULL) {
81 		// We didn't find a free block - we need to allocate
82 		// another chunk, or resize an existing chunk
83 		best = _AllocateChunk(size, newArea);
84 		if (best == NULL)
85 			return NULL;
86 	} else
87 		newArea = false;
88 
89 	// We need to split the chunk into two parts: the one to keep
90 	// and the one to give away
91 
92 	if (best->size == size) {
93 		// The simple case: the free block has exactly the size we wanted to have
94 		fFreeBlocks.Remove(best);
95 		*_address = best;
96 		return best->base;
97 	}
98 
99 	// TODO: maybe we should have the user reserve memory in its object
100 	//	for us, so we don't have to do this here...
101 
102 	struct block* usedBlock = (struct block*)malloc(sizeof(struct block));
103 	if (usedBlock == NULL)
104 		return NULL;
105 
106 	usedBlock->base = best->base;
107 	usedBlock->size = size;
108 	usedBlock->chunk = best->chunk;
109 
110 	best->base += size;
111 	best->size -= size;
112 
113 	*_address = usedBlock;
114 	return usedBlock->base;
115 }
116 
117 
118 void
119 ClientMemoryAllocator::Free(block* freeBlock)
120 {
121 	if (freeBlock == NULL)
122 		return;
123 
124 	// search for an adjacent free block
125 
126 	block_iterator iterator = fFreeBlocks.GetIterator();
127 	struct block* before = NULL;
128 	struct block* after = NULL;
129 
130 	// TODO: this could be done better if free blocks are sorted,
131 	//	and if we had one free blocks list per chunk!
132 	//	IOW this is a bit slow...
133 
134 	while (struct block* block = iterator.Next()) {
135 		if (block->chunk != freeBlock->chunk)
136 			continue;
137 
138 		if (block->base + block->size == freeBlock->base)
139 			before = block;
140 
141 		if (block->base == freeBlock->base + freeBlock->size)
142 			after = block;
143 	}
144 
145 	if (before != NULL && after != NULL) {
146 		// merge with adjacent blocks
147 		before->size += after->size + freeBlock->size;
148 		fFreeBlocks.Remove(after);
149 		free(after);
150 		free(freeBlock);
151 		freeBlock = before;
152 	} else if (before != NULL) {
153 		before->size += freeBlock->size;
154 		free(freeBlock);
155 		freeBlock = before;
156 	} else if (after != NULL) {
157 		after->base -= freeBlock->size;
158 		after->size += freeBlock->size;
159 		free(freeBlock);
160 		freeBlock = after;
161 	} else
162 		fFreeBlocks.Add(freeBlock);
163 
164 	if (freeBlock->size == freeBlock->chunk->size) {
165 		// We can delete the chunk now
166 		struct chunk* chunk = freeBlock->chunk;
167 
168 		fFreeBlocks.Remove(freeBlock);
169 		free(freeBlock);
170 
171 		fChunks.Remove(chunk);
172 		delete_area(chunk->area);
173 		free(chunk);
174 	}
175 }
176 
177 
178 void
179 ClientMemoryAllocator::Dump()
180 {
181 	debug_printf("Application %ld, %s: chunks:\n", fApplication->ClientTeam(),
182 		fApplication->Signature());
183 
184 	chunk_list::Iterator iterator = fChunks.GetIterator();
185 	int32 i = 0;
186 	while (struct chunk* chunk = iterator.Next()) {
187 		debug_printf("  [%4ld] %p, area %ld, base %p, size %lu\n", i++, chunk,
188 			chunk->area, chunk->base, chunk->size);
189 	}
190 
191 	debug_printf("free blocks:\n");
192 
193 	block_list::Iterator blockIterator = fFreeBlocks.GetIterator();
194 	i = 0;
195 	while (struct block* block = blockIterator.Next()) {
196 		debug_printf("  [%6ld] %p, chunk %p, base %p, size %lu\n", i++, block,
197 			block->chunk, block->base, block->size);
198 	}
199 }
200 
201 
202 struct block*
203 ClientMemoryAllocator::_AllocateChunk(size_t size, bool& newArea)
204 {
205 	// round up to multiple of page size
206 	size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
207 
208 	// At first, try to resize our existing areas
209 
210 	chunk_iterator iterator = fChunks.GetIterator();
211 	struct chunk* chunk;
212 	while ((chunk = iterator.Next()) != NULL) {
213 		status_t status = resize_area(chunk->area, chunk->size + size);
214 		if (status == B_OK) {
215 			newArea = false;
216 			break;
217 		}
218 	}
219 
220 	// TODO: resize and relocate while holding the write lock
221 
222 	struct block* block;
223 	uint8* address;
224 
225 	if (chunk == NULL) {
226 		// TODO: temporary measurement as long as resizing areas doesn't
227 		//	work the way we need (with relocating the area, if needed)
228 		if (size < B_PAGE_SIZE * 32)
229 			size = B_PAGE_SIZE * 32;
230 
231 		// create new area for this allocation
232 		chunk = (struct chunk*)malloc(sizeof(struct chunk));
233 		if (chunk == NULL)
234 			return NULL;
235 
236 		block = (struct block*)malloc(sizeof(struct block));
237 		if (block == NULL) {
238 			free(chunk);
239 			return NULL;
240 		}
241 
242 		char name[B_OS_NAME_LENGTH];
243 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
244 		strcpy(name, "client heap");
245 #else
246 		snprintf(name, sizeof(name), "heap:%ld:%s", fApplication->ClientTeam(),
247 			fApplication->SignatureLeaf());
248 #endif
249 		area_id area = create_area(name, (void**)&address, B_ANY_ADDRESS, size,
250 			B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
251 		if (area < B_OK) {
252 			free(block);
253 			free(chunk);
254 			return NULL;
255 		}
256 
257 		// add chunk to list
258 
259 		chunk->area = area;
260 		chunk->base = address;
261 		chunk->size = size;
262 
263 		fChunks.Add(chunk);
264 		newArea = true;
265 	} else {
266 		// create new free block for this chunk
267 		block = (struct block *)malloc(sizeof(struct block));
268 		if (block == NULL)
269 			return NULL;
270 
271 		address = chunk->base + chunk->size;
272 		chunk->size += size;
273 	}
274 
275 	// add block to free list
276 
277 	block->chunk = chunk;
278 	block->base = address;
279 	block->size = size;
280 
281 	fFreeBlocks.Add(block);
282 
283 	return block;
284 }
285 
286 
287 ClientMemory::ClientMemory()
288 	:
289 	fBlock(NULL)
290 {
291 }
292 
293 
294 ClientMemory::~ClientMemory()
295 {
296 	if (fBlock != NULL)
297 		fAllocator->Free(fBlock);
298 }
299 
300 
301 void*
302 ClientMemory::Allocate(ClientMemoryAllocator* allocator, size_t size,
303 	bool& newArea)
304 {
305 	fAllocator = allocator;
306 	return fAllocator->Allocate(size, &fBlock, newArea);
307 }
308 
309 
310 area_id
311 ClientMemory::Area()
312 {
313 	if (fBlock != NULL)
314 		return fBlock->chunk->area;
315 	return B_ERROR;
316 }
317 
318 
319 uint8*
320 ClientMemory::Address()
321 {
322 	if (fBlock != NULL)
323 		return fBlock->base;
324 	return 0;
325 }
326 
327 
328 uint32
329 ClientMemory::AreaOffset()
330 {
331 	if (fBlock != NULL)
332 		return fBlock->base - fBlock->chunk->base;
333 	return 0;
334 }
335 
336 
337 ClonedAreaMemory::ClonedAreaMemory()
338 	:
339 	fClonedArea(-1),
340 	fOffset(0),
341 	fBase(NULL)
342 {
343 }
344 
345 
346 ClonedAreaMemory::~ClonedAreaMemory()
347 {
348 	if (fClonedArea >= 0)
349 		delete_area(fClonedArea);
350 }
351 
352 
353 void*
354 ClonedAreaMemory::Clone(area_id area, uint32 offset)
355 {
356 	fClonedArea = clone_area("server_memory", (void**)&fBase, B_ANY_ADDRESS,
357 		B_READ_AREA | B_WRITE_AREA, area);
358 	if (fBase == NULL)
359 		return NULL;
360 	fOffset = offset;
361 	return Address();
362 }
363 
364 
365 area_id
366 ClonedAreaMemory::Area()
367 {
368 	return fClonedArea;
369 }
370 
371 
372 uint8*
373 ClonedAreaMemory::Address()
374 {
375 	return fBase + fOffset;
376 }
377 
378 
379 uint32
380 ClonedAreaMemory::AreaOffset()
381 {
382 	return fOffset;
383 }
384