xref: /haiku/src/servers/app/ClientMemoryAllocator.cpp (revision 922e7ba1f3228e6f28db69b0ded8f86eb32dea17)
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 	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 
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 
174 	if (freeBlock->size == freeBlock->chunk->size) {
175 		// We can delete the chunk now
176 		struct chunk* chunk = freeBlock->chunk;
177 
178 		fFreeBlocks.Remove(freeBlock);
179 		free(freeBlock);
180 
181 		fChunks.Remove(chunk);
182 		delete_area(chunk->area);
183 		free(chunk);
184 	}
185 }
186 
187 
188 area_id
189 ClientMemoryAllocator::Area(void* cookie)
190 {
191 	struct block* block = (struct block*)cookie;
192 
193 	if (block != NULL)
194 		return block->chunk->area;
195 
196 	return B_ERROR;
197 }
198 
199 
200 uint32
201 ClientMemoryAllocator::AreaOffset(void* cookie)
202 {
203 	struct block* block = (struct block*)cookie;
204 
205 	if (block != NULL)
206 		return block->base - block->chunk->base;
207 
208 	return 0;
209 }
210 
211 
212 bool
213 ClientMemoryAllocator::Lock()
214 {
215 	return fLock.ReadLock();
216 }
217 
218 
219 void
220 ClientMemoryAllocator::Unlock()
221 {
222 	fLock.ReadUnlock();
223 }
224 
225 
226 void
227 ClientMemoryAllocator::Dump()
228 {
229 	AutoReadLocker locker(fLock);
230 
231 	debug_printf("Application %ld, %s: chunks:\n", fApplication->ClientTeam(),
232 		fApplication->Signature());
233 
234 	chunk_list::Iterator iterator = fChunks.GetIterator();
235 	int32 i = 0;
236 	while (struct chunk* chunk = iterator.Next()) {
237 		debug_printf("  [%4ld] %p, area %ld, base %p, size %lu\n", i++, chunk,
238 			chunk->area, chunk->base, chunk->size);
239 	}
240 
241 	debug_printf("free blocks:\n");
242 
243 	block_list::Iterator blockIterator = fFreeBlocks.GetIterator();
244 	i = 0;
245 	while (struct block* block = blockIterator.Next()) {
246 		debug_printf("  [%6ld] %p, chunk %p, base %p, size %lu\n", i++, block,
247 			block->chunk, block->base, block->size);
248 	}
249 }
250 
251 
252 struct block*
253 ClientMemoryAllocator::_AllocateChunk(size_t size, bool& newArea)
254 {
255 	// round up to multiple of page size
256 	size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
257 
258 	// At first, try to resize our existing areas
259 
260 	chunk_iterator iterator = fChunks.GetIterator();
261 	struct chunk* chunk;
262 	while ((chunk = iterator.Next()) != NULL) {
263 		status_t status = resize_area(chunk->area, chunk->size + size);
264 		if (status == B_OK) {
265 			newArea = false;
266 			break;
267 		}
268 	}
269 
270 	// TODO: resize and relocate while holding the write lock
271 
272 	struct block* block;
273 	uint8* address;
274 
275 	if (chunk == NULL) {
276 		// TODO: temporary measurement as long as resizing areas doesn't
277 		//	work the way we need (with relocating the area, if needed)
278 		if (size < B_PAGE_SIZE * 32)
279 			size = B_PAGE_SIZE * 32;
280 
281 		// create new area for this allocation
282 		chunk = (struct chunk*)malloc(sizeof(struct chunk));
283 		if (chunk == NULL)
284 			return NULL;
285 
286 		block = (struct block*)malloc(sizeof(struct block));
287 		if (block == NULL) {
288 			free(chunk);
289 			return NULL;
290 		}
291 
292 		char name[B_OS_NAME_LENGTH];
293 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
294 		strcpy(name, "client heap");
295 #else
296 		snprintf(name, sizeof(name), "heap:%ld:%s", fApplication->ClientTeam(),
297 			fApplication->SignatureLeaf());
298 #endif
299 		area_id area = create_area(name, (void**)&address, B_ANY_ADDRESS, size,
300 			B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
301 		if (area < B_OK) {
302 			free(block);
303 			free(chunk);
304 			return NULL;
305 		}
306 
307 		// add chunk to list
308 
309 		chunk->area = area;
310 		chunk->base = address;
311 		chunk->size = size;
312 
313 		fChunks.Add(chunk);
314 		newArea = true;
315 	} else {
316 		// create new free block for this chunk
317 		block = (struct block *)malloc(sizeof(struct block));
318 		if (block == NULL)
319 			return NULL;
320 
321 		address = chunk->base + chunk->size;
322 		chunk->size += size;
323 	}
324 
325 	// add block to free list
326 
327 	block->chunk = chunk;
328 	block->base = address;
329 	block->size = size;
330 
331 	fFreeBlocks.Add(block);
332 
333 	return block;
334 }
335 
336