ClientMemoryAllocator: use map and reference count

- Resolve TODO about using map for server area_id lookup.

- Remove server cloned area delete request logic because it is less
  efficient and robust. Cloned area delete request messages may be
  missed if client message queue is full so it may cause cloned area
  leak.

- Implement reference counting for cloned areas instead.

Change-Id: Ie434ad36c2761ab0df00d341d55a6cea67b69830
Reviewed-on: https://review.haiku-os.org/c/haiku/+/9667
Reviewed-by: waddlesplash <[email protected]>
Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
X512
2025-10-06 15:10:48 +00:00
committed by waddlesplash
parent 5247c8c14f
commit 47538c534f
10 changed files with 73 additions and 130 deletions
+12 -5
View File
@@ -9,13 +9,22 @@
#define SERVER_MEMORY_ALLOCATOR_H #define SERVER_MEMORY_ALLOCATOR_H
#include <map>
#include <OS.h> #include <OS.h>
#include <List.h>
namespace BPrivate { namespace BPrivate {
struct area_mapping {
int32 reference_count;
area_id server_area;
area_id local_area;
uint8* local_base;
};
class ServerMemoryAllocator { class ServerMemoryAllocator {
public: public:
ServerMemoryAllocator(); ServerMemoryAllocator();
@@ -28,11 +37,9 @@ public:
bool readOnly = false); bool readOnly = false);
void RemoveArea(area_id serverArea); void RemoveArea(area_id serverArea);
status_t AreaAndBaseFor(area_id serverArea,
area_id& area, uint8*& base);
private: private:
BList fAreas; std::map<area_id, area_mapping>
fAreas;
}; };
-7
View File
@@ -377,18 +377,11 @@ enum {
AS_LAST_CODE AS_LAST_CODE
}; };
// TODO: move this into a private app header, together with the rest of the
// private message definitions in AppDefs.h
enum {
kMsgDeleteServerMemoryArea = '_DSA',
};
// bitmap allocation flags // bitmap allocation flags
enum { enum {
kAllocator = 0x1, kAllocator = 0x1,
kFramebuffer = 0x2, kFramebuffer = 0x2,
kHeap = 0x4, kHeap = 0x4,
kNewAllocatorArea = 0x8,
}; };
#endif // APP_SERVER_PROTOCOL_H #endif // APP_SERVER_PROTOCOL_H
-11
View File
@@ -695,17 +695,6 @@ BApplication::MessageReceived(BMessage* message)
_ReconnectToServer(); _ReconnectToServer();
break; break;
case kMsgDeleteServerMemoryArea:
{
int32 serverArea;
if (message->FindInt32("server area", &serverArea) == B_OK) {
// The link is not used, but we currently borrow its lock
BPrivate::AppServerLink link;
fServerAllocator->RemoveArea(serverArea);
}
break;
}
default: default:
BLooper::MessageReceived(message); BLooper::MessageReceived(message);
} }
+28 -47
View File
@@ -31,27 +31,18 @@ static const size_t kReserveMaxSize = 32 * 1024 * 1024;
namespace BPrivate { namespace BPrivate {
struct area_mapping {
area_id server_area;
area_id local_area;
uint8* local_base;
};
ServerMemoryAllocator::ServerMemoryAllocator() ServerMemoryAllocator::ServerMemoryAllocator()
:
fAreas(4)
{ {
} }
ServerMemoryAllocator::~ServerMemoryAllocator() ServerMemoryAllocator::~ServerMemoryAllocator()
{ {
for (int32 i = fAreas.CountItems(); i-- > 0;) { while (!fAreas.empty()) {
area_mapping* mapping = (area_mapping*)fAreas.ItemAt(i); std::map<area_id, area_mapping>::iterator it = fAreas.begin();
area_mapping& mapping = it->second;
delete_area(mapping->local_area); delete_area(mapping.local_area);
delete mapping; fAreas.erase(it);
} }
} }
@@ -67,11 +58,24 @@ status_t
ServerMemoryAllocator::AddArea(area_id serverArea, area_id& _area, ServerMemoryAllocator::AddArea(area_id serverArea, area_id& _area,
uint8*& _base, size_t size, bool readOnly) uint8*& _base, size_t size, bool readOnly)
{ {
area_mapping* mapping = new (std::nothrow) area_mapping; std::map<area_id, area_mapping>::iterator it = fAreas.find(serverArea);
if (mapping == NULL || !fAreas.AddItem(mapping)) { if (it != fAreas.end()) {
delete mapping; area_mapping& mapping = it->second;
mapping.reference_count++;
_area = mapping.local_area;
_base = mapping.local_base;
return B_OK;
}
area_mapping* mapping;
try {
mapping = &fAreas[serverArea];
} catch (const std::bad_alloc&) {
return B_NO_MEMORY; return B_NO_MEMORY;
} }
mapping->reference_count = 1;
status_t status = B_ERROR; status_t status = B_ERROR;
uint32 addressSpec = B_ANY_ADDRESS; uint32 addressSpec = B_ANY_ADDRESS;
@@ -95,8 +99,7 @@ ServerMemoryAllocator::AddArea(area_id serverArea, area_id& _area,
if (mapping->local_area < B_OK) { if (mapping->local_area < B_OK) {
status = mapping->local_area; status = mapping->local_area;
fAreas.RemoveItem(mapping); fAreas.erase(serverArea);
delete mapping;
return status; return status;
} }
@@ -114,37 +117,15 @@ ServerMemoryAllocator::AddArea(area_id serverArea, area_id& _area,
void void
ServerMemoryAllocator::RemoveArea(area_id serverArea) ServerMemoryAllocator::RemoveArea(area_id serverArea)
{ {
for (int32 i = fAreas.CountItems(); i-- > 0;) { std::map<area_id, area_mapping>::iterator it = fAreas.find(serverArea);
area_mapping* mapping = (area_mapping*)fAreas.ItemAt(i); if (it != fAreas.end()) {
area_mapping& mapping = it->second;
if (mapping->server_area == serverArea) { if (mapping.reference_count-- == 1) {
// we found the area we should remove delete_area(mapping.local_area);
delete_area(mapping->local_area); fAreas.erase(serverArea);
delete mapping;
fAreas.RemoveItem(i);
break;
} }
} }
} }
status_t
ServerMemoryAllocator::AreaAndBaseFor(area_id serverArea, area_id& _area,
uint8*& _base)
{
// TODO: why not use a map?
for (int32 i = fAreas.CountItems(); i-- > 0;) {
area_mapping* mapping = (area_mapping*)fAreas.ItemAt(i);
if (mapping->server_area == serverArea) {
_area = mapping->local_area;
_base = mapping->local_base;
return B_OK;
}
}
return B_ERROR;
}
} // namespace BPrivate } // namespace BPrivate
+6 -6
View File
@@ -1164,15 +1164,10 @@ BBitmap::_InitObject(BRect bounds, color_space colorSpace, uint32 flags,
BPrivate::ServerMemoryAllocator* allocator BPrivate::ServerMemoryAllocator* allocator
= BApplication::Private::ServerAllocator(); = BApplication::Private::ServerAllocator();
if ((allocationFlags & kNewAllocatorArea) != 0) {
error = allocator->AddArea(fServerArea, fArea, error = allocator->AddArea(fServerArea, fArea,
fBasePointer, size); fBasePointer, size);
} else {
error = allocator->AreaAndBaseFor(fServerArea, fArea,
fBasePointer);
if (error == B_OK) if (error == B_OK)
fBasePointer += fAreaOffset; fBasePointer += fAreaOffset;
}
if ((allocationFlags & kFramebuffer) != 0) { if ((allocationFlags & kFramebuffer) != 0) {
// The base pointer will now point to an overlay_client_data // The base pointer will now point to an overlay_client_data
@@ -1270,7 +1265,12 @@ BBitmap::_CleanUp()
link.Attach<int32>(fServerToken); link.Attach<int32>(fServerToken);
link.Flush(); link.Flush();
// The server areas are deleted via kMsgDeleteServerMemoryArea message if (fServerArea >= B_OK) {
BPrivate::ServerMemoryAllocator* allocator
= BApplication::Private::ServerAllocator();
allocator->RemoveArea(fServerArea);
}
fArea = -1; fArea = -1;
fServerToken = -1; fServerToken = -1;
+4 -6
View File
@@ -121,13 +121,12 @@ BitmapManager::CreateBitmap(ClientMemoryAllocator* allocator,
overlayToken); overlayToken);
overlay_client_data* clientData = NULL; overlay_client_data* clientData = NULL;
bool newArea = false;
if (overlay != NULL && overlay->InitCheck() == B_OK) { if (overlay != NULL && overlay->InitCheck() == B_OK) {
// allocate client memory to communicate the overlay semaphore // allocate client memory to communicate the overlay semaphore
// and buffer location to the BBitmap // and buffer location to the BBitmap
clientData = (overlay_client_data*)bitmap->fClientMemory.Allocate( clientData = (overlay_client_data*)bitmap->fClientMemory.Allocate(
allocator, sizeof(overlay_client_data), newArea); allocator, sizeof(overlay_client_data));
} }
if (clientData != NULL) { if (clientData != NULL) {
@@ -139,19 +138,18 @@ BitmapManager::CreateBitmap(ClientMemoryAllocator* allocator,
buffer = (uint8*)overlay->OverlayBuffer()->buffer; buffer = (uint8*)overlay->OverlayBuffer()->buffer;
if (_allocationFlags) if (_allocationFlags)
*_allocationFlags = kFramebuffer | (newArea ? kNewAllocatorArea : 0); *_allocationFlags = kFramebuffer;
} else } else
delete overlay; delete overlay;
} else if (allocator != NULL) { } else if (allocator != NULL) {
// standard bitmaps // standard bitmaps
bool newArea;
buffer = (uint8*)bitmap->fClientMemory.Allocate(allocator, buffer = (uint8*)bitmap->fClientMemory.Allocate(allocator,
bitmap->BitsLength(), newArea); bitmap->BitsLength());
if (buffer != NULL) { if (buffer != NULL) {
bitmap->fMemory = &bitmap->fClientMemory; bitmap->fMemory = &bitmap->fClientMemory;
if (_allocationFlags) if (_allocationFlags)
*_allocationFlags = kAllocator | (newArea ? kNewAllocatorArea : 0); *_allocationFlags = kAllocator;
} }
} else { } else {
// server side only bitmaps // server side only bitmaps
+7 -15
View File
@@ -64,7 +64,7 @@ ClientMemoryAllocator::~ClientMemoryAllocator()
void* void*
ClientMemoryAllocator::Allocate(size_t size, block** _address, bool& newArea) ClientMemoryAllocator::Allocate(size_t size, block** _address)
{ {
// A detached allocator no longer allows any further allocations // A detached allocator no longer allows any further allocations
if (fApplication == NULL) if (fApplication == NULL)
@@ -86,11 +86,10 @@ ClientMemoryAllocator::Allocate(size_t size, block** _address, bool& newArea)
if (best == NULL) { if (best == NULL) {
// We didn't find a free block - we need to allocate // We didn't find a free block - we need to allocate
// another chunk, or resize an existing chunk // another chunk, or resize an existing chunk
best = _AllocateChunk(size, newArea); best = _AllocateChunk(size);
if (best == NULL) if (best == NULL)
return NULL; return NULL;
} else }
newArea = false;
// We need to split the chunk into two parts: the one to keep // We need to split the chunk into two parts: the one to keep
// and the one to give away // and the one to give away
@@ -184,9 +183,6 @@ ClientMemoryAllocator::Free(block* freeBlock)
fChunks.Remove(chunk); fChunks.Remove(chunk);
delete_area(chunk->area); delete_area(chunk->area);
if (fApplication != NULL)
fApplication->NotifyDeleteClientArea(chunk->area);
free(chunk); free(chunk);
} }
} }
@@ -227,7 +223,7 @@ ClientMemoryAllocator::Dump()
struct block* struct block*
ClientMemoryAllocator::_AllocateChunk(size_t size, bool& newArea) ClientMemoryAllocator::_AllocateChunk(size_t size)
{ {
// round up to multiple of page size // round up to multiple of page size
size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
@@ -238,11 +234,9 @@ ClientMemoryAllocator::_AllocateChunk(size_t size, bool& newArea)
struct chunk* chunk; struct chunk* chunk;
while ((chunk = iterator.Next()) != NULL) { while ((chunk = iterator.Next()) != NULL) {
status_t status = resize_area(chunk->area, chunk->size + size); status_t status = resize_area(chunk->area, chunk->size + size);
if (status == B_OK) { if (status == B_OK)
newArea = false;
break; break;
} }
}
// TODO: resize and relocate while holding the write lock // TODO: resize and relocate while holding the write lock
@@ -288,7 +282,6 @@ ClientMemoryAllocator::_AllocateChunk(size_t size, bool& newArea)
chunk->size = size; chunk->size = size;
fChunks.Add(chunk); fChunks.Add(chunk);
newArea = true;
} else { } else {
// create new free block for this chunk // create new free block for this chunk
block = (struct block *)malloc(sizeof(struct block)); block = (struct block *)malloc(sizeof(struct block));
@@ -333,12 +326,11 @@ ClientMemory::~ClientMemory()
void* void*
ClientMemory::Allocate(ClientMemoryAllocator* allocator, size_t size, ClientMemory::Allocate(ClientMemoryAllocator* allocator, size_t size)
bool& newArea)
{ {
fAllocator.SetTo(allocator, false); fAllocator.SetTo(allocator, false);
return fAllocator->Allocate(size, &fBlock, newArea); return fAllocator->Allocate(size, &fBlock);
} }
+3 -4
View File
@@ -40,8 +40,7 @@ public:
ClientMemoryAllocator(ServerApp* application); ClientMemoryAllocator(ServerApp* application);
~ClientMemoryAllocator(); ~ClientMemoryAllocator();
void* Allocate(size_t size, block** _address, void* Allocate(size_t size, block** _address);
bool& newArea);
void Free(block* cookie); void Free(block* cookie);
void Detach(); void Detach();
@@ -49,7 +48,7 @@ public:
void Dump(); void Dump();
private: private:
struct block* _AllocateChunk(size_t size, bool& newArea); struct block* _AllocateChunk(size_t size);
private: private:
ServerApp* fApplication; ServerApp* fApplication;
@@ -76,7 +75,7 @@ public:
virtual ~ClientMemory(); virtual ~ClientMemory();
void* Allocate(ClientMemoryAllocator* allocator, void* Allocate(ClientMemoryAllocator* allocator,
size_t size, bool& newArea); size_t size);
virtual area_id Area(); virtual area_id Area();
virtual uint8* Address(); virtual uint8* Address();
-15
View File
@@ -473,21 +473,6 @@ ServerApp::RemovePicture(ServerPicture* picture)
} }
/*! Called from the ClientMemoryAllocator whenever a server area could be
deleted.
A message is then sent to the client telling it that it can delete its
client area, too.
*/
void
ServerApp::NotifyDeleteClientArea(area_id serverArea)
{
BMessage notify(kMsgDeleteServerMemoryArea);
notify.AddInt32("server area", serverArea);
SendMessageToClient(&notify);
}
/*! \brief Send a message to the ServerApp's BApplication /*! \brief Send a message to the ServerApp's BApplication
\param message The message to send \param message The message to send
*/ */
-1
View File
@@ -96,7 +96,6 @@ public:
BPrivate::BTokenSpace& ViewTokens() { return fViewTokens; } BPrivate::BTokenSpace& ViewTokens() { return fViewTokens; }
void NotifyDeleteClientArea(area_id serverArea);
AppFontManager* FontManager() { return fAppFontManager; } AppFontManager* FontManager() { return fAppFontManager; }
private: private: