xref: /haiku/src/servers/app/ClientMemoryAllocator.cpp (revision b30304acc8c37e678a1bf66976d15bdab103f931)
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 <stdlib.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 status_t
67 ClientMemoryAllocator::InitCheck()
68 {
69 	return fLock.InitCheck() < B_OK ? fLock.InitCheck() : B_OK;
70 }
71 
72 
73 void *
74 ClientMemoryAllocator::Allocate(size_t size, void** _address, bool& newArea)
75 {
76 	// Search best matching free block from the list
77 
78 	block_iterator iterator = fFreeBlocks.GetIterator();
79 	struct block* block;
80 	struct block* best = NULL;
81 
82 	while ((block = iterator.Next()) != NULL) {
83 		if (block->size >= size && (best == NULL || block->size < best->size))
84 			best = block;
85 	}
86 
87 	if (best == NULL) {
88 		// We didn't find a free block - we need to allocate
89 		// another chunk, or resize an existing chunk
90 		best = _AllocateChunk(size, newArea);
91 		if (best == NULL)
92 			return NULL;
93 	} else
94 		newArea = false;
95 
96 	// We need to split the chunk into two parts: the one to keep
97 	// and the one to give away
98 
99 	if (best->size == size) {
100 		// The simple case: the free block has exactly the size we wanted to have
101 		fFreeBlocks.Remove(best);
102 		*_address = best->base;
103 		return best;
104 	}
105 
106 	// TODO: maybe we should have the user reserve memory in its object
107 	//	for us, so we don't have to do this here...
108 
109 	struct block* usedBlock = (struct block*)malloc(sizeof(struct block));
110 	if (usedBlock == NULL)
111 		return NULL;
112 
113 	usedBlock->base = best->base;
114 	usedBlock->size = size;
115 	usedBlock->chunk = best->chunk;
116 
117 	best->base += size;
118 	best->size -= size;
119 
120 	*_address = usedBlock->base;
121 	return usedBlock;
122 }
123 
124 
125 void
126 ClientMemoryAllocator::Free(void *cookie)
127 {
128 	if (cookie == NULL)
129 		return;
130 
131 	struct block* freeBlock = (struct block*)cookie;
132 
133 	// search for an adjacent free block
134 
135 	block_iterator iterator = fFreeBlocks.GetIterator();
136 	struct block* before = NULL;
137 	struct block* after = NULL;
138 	struct block* block;
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 ((block = iterator.Next()) != NULL) {
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 	} else if (before != NULL) {
162 		before->size += freeBlock->size;
163 		free(freeBlock);
164 	} else if (after != NULL) {
165 		after->base -= freeBlock->size;
166 		after->size += freeBlock->size;
167 		free(freeBlock);
168 	} else
169 		fFreeBlocks.Add(freeBlock);
170 
171 	// TODO: check if the whole chunk is free now (we could delete it then)
172 }
173 
174 
175 area_id
176 ClientMemoryAllocator::Area(void* cookie)
177 {
178 	struct block* block = (struct block*)cookie;
179 
180 	if (block != NULL)
181 		return block->chunk->area;
182 
183 	return B_ERROR;
184 }
185 
186 
187 uint32
188 ClientMemoryAllocator::AreaOffset(void* cookie)
189 {
190 	struct block* block = (struct block*)cookie;
191 
192 	if (block != NULL)
193 		return block->base - block->chunk->base;
194 
195 	return 0;
196 }
197 
198 
199 bool
200 ClientMemoryAllocator::Lock()
201 {
202 	return fLock.ReadLock();
203 }
204 
205 
206 void
207 ClientMemoryAllocator::Unlock()
208 {
209 	fLock.ReadUnlock();
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