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:
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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(¬ify);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! \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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user