BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / drivers / graphics / common / memory_manager.c
blob55bfb3097f1eca72e870333c78e38b7b6eabb1b1
1 /*
2 * Copyright 2006, Haiku Inc.
3 * Copyright 2002, Thomas Kurschel.
5 * Distributed under the terms of the MIT license.
6 */
8 /** Memory manager used for graphics mem
10 * It has the following features
11 * - doesn't access memory to be managed
12 * - memory block's owner is identified by tag,
13 * tag is verified during free, and all memory
14 * belonging to one tag can be freed at once
15 * - multi-threading save
18 #include "memory_manager.h"
20 #include <KernelExport.h>
21 #include <stdlib.h>
24 //#define TRACE_MEMORY_MANAGER
25 #ifdef TRACE_MEMORY_MANAGER
26 # define TRACE(x) dprintf x
27 #else
28 # define TRACE(x) ;
29 #endif
32 // allocated memory block
33 typedef struct mem_block {
34 struct mem_block *prev, *next;
35 uint32 base;
36 uint32 size;
37 void *tag;
38 bool allocated;
39 } mem_block;
41 // memory heap
42 struct mem_info {
43 mem_block *first;
44 area_id heap_area;
45 uint32 block_size;
46 sem_id lock;
47 mem_block *heap;
48 mem_block *unused;
49 uint32 heap_entries;
53 /** merge "block" with successor */
55 static void
56 merge(mem_info *mem, mem_block *block)
58 mem_block *next = block->next;
60 block->size += next->size;
62 if (next->next)
63 next->next->prev = block;
65 block->next = next->next;
66 next->next = mem->unused;
67 mem->unused = next;
71 /** free memory block including merge */
73 static mem_block *
74 freeblock(mem_info *mem, mem_block *block)
76 mem_block *prev, *next;
78 block->allocated = false;
79 prev = block->prev;
81 if (prev && !prev->allocated) {
82 block = prev;
83 merge(mem, prev);
86 next = block->next;
88 if (next && !next->allocated)
89 merge(mem, block);
91 return block;
95 // #pragma mark -
98 /** Init memory manager.
100 * \param start start of address space
101 * \param length length of address space
102 * \param blockSize - granularity
103 * \param heapEntries - maximum number of blocks
106 mem_info *
107 mem_init(const char* name, uint32 start, uint32 length,
108 uint32 blockSize, uint32 heapEntries)
110 mem_block *first;
111 mem_info *mem;
112 uint i;
113 uint32 size;
115 TRACE(("mem_init(name=%s, start=%lx, length=%lx, blockSize=%lx, heapEntries=%ld)\n",
116 name, start, length, blockSize, heapEntries));
118 mem = malloc(sizeof(*mem));
119 if (mem == NULL)
120 goto err1;
122 mem->block_size = blockSize;
123 mem->heap_entries = heapEntries;
124 mem->lock = create_sem(1, name);
125 if (mem->lock < 0)
126 goto err2;
128 // align size to B_PAGE_SIZE
129 size = heapEntries * sizeof(mem_block);
130 size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
132 mem->heap_area = create_area(name, (void **)&mem->heap,
133 B_ANY_ADDRESS, size, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA);
135 if (mem->heap_area < 0 || mem->heap == NULL)
136 goto err3;
138 for (i = 1; i < heapEntries; ++i) {
139 mem->heap[i - 1].next = &mem->heap[i];
142 mem->heap[heapEntries - 1].next = NULL;
143 mem->unused = &mem->heap[1];
145 first = &mem->heap[0];
146 mem->first = first;
148 first->base = start;
149 first->size = length;
150 first->prev = first->next = NULL;
151 first->allocated = false;
153 return mem;
155 err3:
156 delete_sem(mem->lock);
157 err2:
158 free(mem);
159 err1:
160 return NULL;
164 /** destroy heap */
166 void
167 mem_destroy(mem_info *mem)
169 TRACE(("mem_destroy(mem %p)\n", mem));
171 delete_area(mem->heap_area);
172 delete_sem(mem->lock);
173 free(mem);
177 /** Allocate memory block
179 * \param mem heap handle
180 * \param size size in bytes
181 * \param tag owner tag
183 * \param blockID - returns block id
184 * \param offset - returns start address of block
187 status_t
188 mem_alloc(mem_info *mem, uint32 size, void *tag, uint32 *blockID, uint32 *offset)
190 mem_block *current, *newEntry;
191 status_t status;
193 TRACE(("mem_alloc(mem %p, size=%lx, tag=%p)\n", mem, size, tag));
195 status = acquire_sem(mem->lock);
196 if (status != B_OK)
197 return status;
199 // we assume block_size is power of two
200 size = (size + mem->block_size - 1) & ~(mem->block_size - 1);
202 // simple first fit
203 for (current = mem->first; current; current = current->next) {
204 if (!current->allocated && current->size >= size)
205 break;
208 if (current == NULL) {
209 TRACE(("mem_alloc: out of memory\n"));
210 goto err;
213 if (size != current->size) {
214 newEntry = mem->unused;
216 if (newEntry == NULL) {
217 TRACE(("mem_alloc: out of blocks\n"));
218 goto err;
221 mem->unused = newEntry->next;
223 newEntry->next = current->next;
224 newEntry->prev = current;
225 newEntry->allocated = false;
226 newEntry->base = current->base + size;
227 newEntry->size = current->size - size;
229 if (current->next)
230 current->next->prev = newEntry;
232 current->next = newEntry;
233 current->size = size;
236 current->allocated = true;
237 current->tag = tag;
239 *blockID = current - mem->heap + 1;
240 *offset = current->base;
242 release_sem(mem->lock);
244 TRACE(("mem_alloc(block_id=%ld, offset=%lx)\n", *blockID, *offset));
245 return B_OK;
247 err:
248 release_sem(mem->lock);
249 return B_NO_MEMORY;
253 /** Free memory
254 * \param mem heap handle
255 * \param blockID block id
256 * \param tag owner tag (must match tag passed to mem_alloc())
259 status_t
260 mem_free(mem_info *mem, uint32 blockID, void *tag)
262 mem_block *block;
263 status_t status;
265 TRACE(("mem_free(mem %p, blockID=%ld, tag=%p)\n", mem, blockID, tag));
267 status = acquire_sem(mem->lock);
268 if (status != B_OK)
269 return status;
271 --blockID;
273 if (blockID >= mem->heap_entries) {
274 TRACE(("mem_free: invalid ID %lu\n", blockID));
275 goto err;
278 block = &mem->heap[blockID];
279 if (!block->allocated || block->tag != tag) {
280 TRACE(("mem_free: not owner\n"));
281 goto err;
284 freeblock(mem, block);
286 release_sem(mem->lock);
287 return B_OK;
289 err:
290 release_sem(mem->lock);
291 return B_BAD_VALUE;
295 /** Free all memory belonging to owner "tag" */
297 status_t
298 mem_freetag(mem_info *mem, void *tag)
300 mem_block *current;
301 status_t status;
303 TRACE(("mem_freetag(mem %p, tag=%p)\n", mem, tag));
305 status = acquire_sem(mem->lock);
306 if (status != B_OK)
307 return status;
309 for (current = mem->first; current; current = current->next) {
310 if (current->allocated && current->tag == tag)
311 current = freeblock(mem, current);
314 release_sem(mem->lock);
315 return B_OK;