xref: /haiku/src/servers/app/ClientMemoryAllocator.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
1 /*
2  * Copyright 2006-2007, 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 	struct block* block;
140 
141 	// TODO: this could be done better if free blocks are sorted,
142 	//	and if we had one free blocks list per chunk!
143 	//	IOW this is a bit slow...
144 
145 	while ((block = iterator.Next()) != NULL) {
146 		if (block->chunk != freeBlock->chunk)
147 			continue;
148 
149 		if (block->base + block->size == freeBlock->base)
150 			before = block;
151 
152 		if (block->base == freeBlock->base + freeBlock->size)
153 			after = block;
154 	}
155 
156 	if (before != NULL && after != NULL) {
157 		// merge with adjacent blocks
158 		before->size += after->size + freeBlock->size;
159 		fFreeBlocks.Remove(after);
160 		free(after);
161 		free(freeBlock);
162 	} else if (before != NULL) {
163 		before->size += freeBlock->size;
164 		free(freeBlock);
165 	} else if (after != NULL) {
166 		after->base -= freeBlock->size;
167 		after->size += freeBlock->size;
168 		free(freeBlock);
169 	} else
170 		fFreeBlocks.Add(freeBlock);
171 
172 	// TODO: check if the whole chunk is free now (we could delete it then)
173 }
174 
175 
176 area_id
177 ClientMemoryAllocator::Area(void* cookie)
178 {
179 	struct block* block = (struct block*)cookie;
180 
181 	if (block != NULL)
182 		return block->chunk->area;
183 
184 	return B_ERROR;
185 }
186 
187 
188 uint32
189 ClientMemoryAllocator::AreaOffset(void* cookie)
190 {
191 	struct block* block = (struct block*)cookie;
192 
193 	if (block != NULL)
194 		return block->base - block->chunk->base;
195 
196 	return 0;
197 }
198 
199 
200 bool
201 ClientMemoryAllocator::Lock()
202 {
203 	return fLock.ReadLock();
204 }
205 
206 
207 void
208 ClientMemoryAllocator::Unlock()
209 {
210 	fLock.ReadUnlock();
211 }
212 
213 
214 struct block *
215 ClientMemoryAllocator::_AllocateChunk(size_t size, bool& newArea)
216 {
217 	// round up to multiple of page size
218 	size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
219 
220 	// At first, try to resize our existing areas
221 
222 	chunk_iterator iterator = fChunks.GetIterator();
223 	struct chunk* chunk;
224 	while ((chunk = iterator.Next()) != NULL) {
225 		status_t status = resize_area(chunk->area, chunk->size + size);
226 		if (status == B_OK) {
227 			newArea = false;
228 			break;
229 		}
230 	}
231 
232 	// TODO: resize and relocate while holding the write lock
233 
234 	struct block* block;
235 	uint8* address;
236 
237 	if (chunk == NULL) {
238 		// TODO: temporary measurement as long as resizing areas doesn't
239 		//	work the way we need (with relocating the area, if needed)
240 		if (size < B_PAGE_SIZE * 32)
241 			size = B_PAGE_SIZE * 32;
242 
243 		// create new area for this allocation
244 		chunk = (struct chunk*)malloc(sizeof(struct chunk));
245 		if (chunk == NULL)
246 			return NULL;
247 
248 		block = (struct block*)malloc(sizeof(struct block));
249 		if (block == NULL) {
250 			free(chunk);
251 			return NULL;
252 		}
253 
254 		char name[B_OS_NAME_LENGTH];
255 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
256 		strcpy(name, "client heap");
257 #else
258 		snprintf(name, sizeof(name), "heap:%ld:%s", fApplication->ClientTeam(),
259 			fApplication->SignatureLeaf());
260 #endif
261 		area_id area = create_area(name, (void**)&address, B_ANY_ADDRESS, size,
262 			B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
263 		if (area < B_OK) {
264 			free(block);
265 			free(chunk);
266 			return NULL;
267 		}
268 
269 		// add chunk to list
270 
271 		chunk->area = area;
272 		chunk->base = address;
273 		chunk->size = size;
274 
275 		fChunks.Add(chunk);
276 		newArea = true;
277 	} else {
278 		// create new free block for this chunk
279 		block = (struct block *)malloc(sizeof(struct block));
280 		if (block == NULL)
281 			return NULL;
282 
283 		address = chunk->base + chunk->size;
284 		chunk->size += size;
285 	}
286 
287 	// add block to free list
288 
289 	block->chunk = chunk;
290 	block->base = address;
291 	block->size = size;
292 
293 	fFreeBlocks.Add(block);
294 
295 	return block;
296 }
297 
298