2 * Copyright 2006-2013, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler, axeld@pinc-software.de
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.
16 // TODO: areas could be relocated if needed (to be able to resize them)
17 // However, this would require a lock whenever a block of memory
18 // allocated by this allocator is accessed.
21 #include "ClientMemoryAllocator.h"
28 #include "ServerApp.h"
31 typedef block_list::Iterator block_iterator
;
32 typedef chunk_list::Iterator chunk_iterator
;
35 ClientMemoryAllocator::ClientMemoryAllocator(ServerApp
* application
)
37 fApplication(application
),
38 fLock("client memory lock")
43 ClientMemoryAllocator::~ClientMemoryAllocator()
45 // delete all areas and chunks/blocks that are still allocated
48 struct block
* block
= fFreeBlocks
.RemoveHead();
56 struct chunk
* chunk
= fChunks
.RemoveHead();
60 delete_area(chunk
->area
);
67 ClientMemoryAllocator::Allocate(size_t size
, block
** _address
, bool& newArea
)
69 // A detached allocator no longer allows any further allocations
70 if (fApplication
== NULL
)
73 BAutolock
locker(fLock
);
75 // Search best matching free block from the list
77 block_iterator iterator
= fFreeBlocks
.GetIterator();
79 struct block
* best
= NULL
;
81 while ((block
= iterator
.Next()) != NULL
) {
82 if (block
->size
>= size
&& (best
== NULL
|| block
->size
< best
->size
))
87 // We didn't find a free block - we need to allocate
88 // another chunk, or resize an existing chunk
89 best
= _AllocateChunk(size
, newArea
);
95 // We need to split the chunk into two parts: the one to keep
96 // and the one to give away
98 if (best
->size
== size
) {
99 // The simple case: the free block has exactly the size we wanted to have
100 fFreeBlocks
.Remove(best
);
105 // TODO: maybe we should have the user reserve memory in its object
106 // for us, so we don't have to do this here...
108 struct block
* usedBlock
= (struct block
*)malloc(sizeof(struct block
));
109 if (usedBlock
== NULL
)
112 usedBlock
->base
= best
->base
;
113 usedBlock
->size
= size
;
114 usedBlock
->chunk
= best
->chunk
;
119 *_address
= usedBlock
;
120 return usedBlock
->base
;
125 ClientMemoryAllocator::Free(block
* freeBlock
)
127 if (freeBlock
== NULL
)
130 BAutolock
locker(fLock
);
132 // search for an adjacent free block
134 block_iterator iterator
= fFreeBlocks
.GetIterator();
135 struct block
* before
= NULL
;
136 struct block
* after
= NULL
;
137 bool inFreeList
= true;
139 if (freeBlock
->size
!= freeBlock
->chunk
->size
) {
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...
144 while (struct block
* block
= iterator
.Next()) {
145 if (block
->chunk
!= freeBlock
->chunk
)
148 if (block
->base
+ block
->size
== freeBlock
->base
)
151 if (block
->base
== freeBlock
->base
+ freeBlock
->size
)
155 if (before
!= NULL
&& after
!= NULL
) {
156 // merge with adjacent blocks
157 before
->size
+= after
->size
+ freeBlock
->size
;
158 fFreeBlocks
.Remove(after
);
162 } else if (before
!= NULL
) {
163 before
->size
+= freeBlock
->size
;
166 } else if (after
!= NULL
) {
167 after
->base
-= freeBlock
->size
;
168 after
->size
+= freeBlock
->size
;
172 fFreeBlocks
.Add(freeBlock
);
176 if (freeBlock
->size
== freeBlock
->chunk
->size
) {
177 // We can delete the chunk now
178 struct chunk
* chunk
= freeBlock
->chunk
;
181 fFreeBlocks
.Remove(freeBlock
);
184 fChunks
.Remove(chunk
);
185 delete_area(chunk
->area
);
187 if (fApplication
!= NULL
)
188 fApplication
->NotifyDeleteClientArea(chunk
->area
);
196 ClientMemoryAllocator::Detach()
198 BAutolock
locker(fLock
);
204 ClientMemoryAllocator::Dump()
206 if (fApplication
!= NULL
) {
207 debug_printf("Application %" B_PRId32
", %s: chunks:\n",
208 fApplication
->ClientTeam(), fApplication
->Signature());
211 chunk_list::Iterator iterator
= fChunks
.GetIterator();
213 while (struct chunk
* chunk
= iterator
.Next()) {
214 debug_printf(" [%4" B_PRId32
"] %p, area %" B_PRId32
", base %p, "
215 "size %lu\n", i
++, chunk
, chunk
->area
, chunk
->base
, chunk
->size
);
218 debug_printf("free blocks:\n");
220 block_list::Iterator blockIterator
= fFreeBlocks
.GetIterator();
222 while (struct block
* block
= blockIterator
.Next()) {
223 debug_printf(" [%6" B_PRId32
"] %p, chunk %p, base %p, size %lu\n",
224 i
++, block
, block
->chunk
, block
->base
, block
->size
);
230 ClientMemoryAllocator::_AllocateChunk(size_t size
, bool& newArea
)
232 // round up to multiple of page size
233 size
= (size
+ B_PAGE_SIZE
- 1) & ~(B_PAGE_SIZE
- 1);
235 // At first, try to resize our existing areas
237 chunk_iterator iterator
= fChunks
.GetIterator();
239 while ((chunk
= iterator
.Next()) != NULL
) {
240 status_t status
= resize_area(chunk
->area
, chunk
->size
+ size
);
241 if (status
== B_OK
) {
247 // TODO: resize and relocate while holding the write lock
253 // TODO: temporary measurement as long as resizing areas doesn't
254 // work the way we need (with relocating the area, if needed)
255 if (size
< B_PAGE_SIZE
* 32)
256 size
= B_PAGE_SIZE
* 32;
258 // create new area for this allocation
259 chunk
= (struct chunk
*)malloc(sizeof(struct chunk
));
263 block
= (struct block
*)malloc(sizeof(struct block
));
269 char name
[B_OS_NAME_LENGTH
];
270 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
271 strcpy(name
, "client heap");
273 snprintf(name
, sizeof(name
), "heap:%" B_PRId32
":%s",
274 fApplication
->ClientTeam(), fApplication
->SignatureLeaf());
276 area_id area
= create_area(name
, (void**)&address
, B_ANY_ADDRESS
, size
,
277 B_NO_LOCK
, B_READ_AREA
| B_WRITE_AREA
);
287 chunk
->base
= address
;
293 // create new free block for this chunk
294 block
= (struct block
*)malloc(sizeof(struct block
));
298 address
= chunk
->base
+ chunk
->size
;
302 // add block to free list
304 block
->chunk
= chunk
;
305 block
->base
= address
;
308 fFreeBlocks
.Add(block
);
317 ClientMemory::ClientMemory()
325 ClientMemory::~ClientMemory()
328 fAllocator
->Free(fBlock
);
329 if (fAllocator
!= NULL
)
330 fAllocator
->ReleaseReference();
335 ClientMemory::Allocate(ClientMemoryAllocator
* allocator
, size_t size
,
338 fAllocator
= allocator
;
339 fAllocator
->AcquireReference();
341 return fAllocator
->Allocate(size
, &fBlock
, newArea
);
349 return fBlock
->chunk
->area
;
355 ClientMemory::Address()
364 ClientMemory::AreaOffset()
367 return fBlock
->base
- fBlock
->chunk
->base
;
375 ClonedAreaMemory::ClonedAreaMemory()
384 ClonedAreaMemory::~ClonedAreaMemory()
386 if (fClonedArea
>= 0)
387 delete_area(fClonedArea
);
392 ClonedAreaMemory::Clone(area_id area
, uint32 offset
)
394 fClonedArea
= clone_area("server_memory", (void**)&fBase
, B_ANY_ADDRESS
,
395 B_READ_AREA
| B_WRITE_AREA
, area
);
404 ClonedAreaMemory::Area()
411 ClonedAreaMemory::Address()
413 return fBase
+ fOffset
;
418 ClonedAreaMemory::AreaOffset()