diff --git a/headers/private/app/ServerProtocol.h b/headers/private/app/ServerProtocol.h index 141fd18602..32e52d8f48 100644 --- a/headers/private/app/ServerProtocol.h +++ b/headers/private/app/ServerProtocol.h @@ -199,8 +199,6 @@ enum { AS_SCREENMODE_CHANGED, // Graphics calls - AS_BEGIN_TRANSACTION, - AS_END_TRANSACTION, AS_SET_HIGH_COLOR, AS_SET_LOW_COLOR, AS_SET_VIEW_COLOR, diff --git a/src/servers/app/Desktop.cpp b/src/servers/app/Desktop.cpp index 7b2a21a099..b080a966d0 100644 --- a/src/servers/app/Desktop.cpp +++ b/src/servers/app/Desktop.cpp @@ -50,6 +50,9 @@ # define STRACE(a) ; #endif +#if !USE_MULTI_LOCKER +# define AutoWriteLocker BAutolock +#endif class KeyboardFilter : public EventFilter { public: @@ -1850,18 +1853,14 @@ Desktop::ViewUnderMouse(const WindowLayer* window) WindowLayer * Desktop::FindWindowLayerByClientToken(int32 token, team_id teamID) { - LockSingleWindow(); - for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; window = window->NextWindow(kAllWindowList)) { if (window->ServerWindow()->ClientToken() == token && window->ServerWindow()->ClientTeam() == teamID) { - UnlockSingleWindow(); return window; } } - UnlockSingleWindow(); return NULL; } @@ -1869,7 +1868,7 @@ Desktop::FindWindowLayerByClientToken(int32 token, team_id teamID) void Desktop::MinimizeApplication(team_id team) { - BAutolock locker(fWindowLock); + AutoWriteLocker locker(fWindowLock); // Just minimize all windows of that application @@ -1886,7 +1885,7 @@ Desktop::MinimizeApplication(team_id team) void Desktop::BringApplicationToFront(team_id team) { - BAutolock locker(fWindowLock); + AutoWriteLocker locker(fWindowLock); // TODO: for now, just maximize all windows of that application @@ -1929,7 +1928,7 @@ Desktop::WindowAction(int32 windowToken, int32 action) void Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender) { - BAutolock locker(fWindowLock); + AutoWriteLocker locker(fWindowLock); // compute the number of windows @@ -1961,7 +1960,7 @@ Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender) void Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender) { - BAutolock locker(fWindowLock); + AutoWriteLocker locker(fWindowLock); BAutolock tokenLocker(BPrivate::gDefaultTokens); ::ServerWindow* window; diff --git a/src/servers/app/Desktop.h b/src/servers/app/Desktop.h index d49c04ad70..b876f9ef5a 100644 --- a/src/servers/app/Desktop.h +++ b/src/servers/app/Desktop.h @@ -31,7 +31,7 @@ #include #include -#define USE_MULTI_LOCKER 0 +#define USE_MULTI_LOCKER 1 #if USE_MULTI_LOCKER # include "MultiLocker.h" diff --git a/src/servers/app/Jamfile b/src/servers/app/Jamfile index 86b8d82f19..db3644d302 100644 --- a/src/servers/app/Jamfile +++ b/src/servers/app/Jamfile @@ -38,6 +38,7 @@ Server app_server : PNGDump.cpp RAMLinkMsgReader.cpp RGBColor.cpp + RegionPool.cpp ScreenManager.cpp ServerApp.cpp ServerBitmap.cpp diff --git a/src/servers/app/MultiLocker.cpp b/src/servers/app/MultiLocker.cpp index 3213bbf353..396c03d18c 100644 --- a/src/servers/app/MultiLocker.cpp +++ b/src/servers/app/MultiLocker.cpp @@ -11,7 +11,7 @@ #include //#define TIMING 1 -#define DEBUG 1 +//#define DEBUG 1 MultiLocker::MultiLocker(const char* semaphoreBaseName) @@ -132,6 +132,11 @@ MultiLocker::ReadLock() if (fInit == B_OK) { if (IsWriteLocked()) { //the writer simply increments the nesting + #if DEBUG + if (fWriterNest < 0) + debugger("ReadLock() - negative writer nest count\n"); + #endif + fWriterNest++; locked = true; } else { @@ -174,10 +179,22 @@ MultiLocker::WriteLock() if (IsWriteLocked(&stack_base, &thread)) { //already the writer - increment the nesting count + #if DEBUG + if (fWriterNest < 0) + debugger("WriteLock() - negative nest count\n"); + #endif + fWriterNest++; locked = true; } else { //new writer acquiring the lock + #if DEBUG + // NOTE: IsReadLocked() tells you + // if this thread really holds the + // "read lock" only in DEBUG mode! + if (IsReadLocked()) + debugger("Reader wants to become writer!"); + #endif if (atomic_add(&fLockCount, 1) >= 1) { //another writer in the lock - acquire the semaphore locked = (acquire_sem_etc(fWriterLock, 1, B_DO_NOT_RESCHEDULE, @@ -227,6 +244,12 @@ MultiLocker::ReadUnlock() if (IsWriteLocked()) { //writers simply decrement the nesting count fWriterNest--; + + #if DEBUG + if (fWriterNest < 0) + debugger("ReadUnlock() - negative writer nest count\n"); + #endif + unlocked = true; } else { //decrement and retrieve the read counter @@ -237,7 +260,7 @@ MultiLocker::ReadUnlock() B_DO_NOT_RESCHEDULE) == B_OK); } else unlocked = true; - #ifdef DEBUG + #if DEBUG //unregister if we released the lock if (unlocked) unregister_thread(); #endif @@ -265,6 +288,12 @@ MultiLocker::WriteUnlock() //if this is a nested lock simply decrement the nest count if (fWriterNest > 0) { fWriterNest--; + + #if DEBUG + if (fWriterNest < 0) + debugger("WriteUnlock(): nest count now negative\n"); + #endif + unlocked = true; } else { //writer finally unlocking @@ -416,14 +445,16 @@ MultiLocker::IsReadLocked() void MultiLocker::register_thread() { - #ifdef DEBUG + #if DEBUG #if TIMING bigtime_t start = system_time(); #endif thread_id thread = find_thread(NULL); - ASSERT_WITH_MESSAGE(fDebugArray[thread%fMaxThreads] == 0,"Nested ReadLock!\n"); + if (fDebugArray[thread%fMaxThreads] != 0) + debugger("Nested ReadLock!\n"); + fDebugArray[thread%fMaxThreads]++; #if TIMING @@ -439,7 +470,7 @@ MultiLocker::register_thread() void MultiLocker::unregister_thread() { - #ifdef DEBUG + #if DEBUG #if TIMING bigtime_t start = system_time(); #endif diff --git a/src/servers/app/MultiLocker.h b/src/servers/app/MultiLocker.h index 22406dfc32..9a55090fbc 100644 --- a/src/servers/app/MultiLocker.h +++ b/src/servers/app/MultiLocker.h @@ -1,10 +1,20 @@ /* MultiLocker.h */ /* + Copyright 2005-2006, Haiku. + Distributed under the terms of the MIT license. + Copyright 1999, Be Incorporated. All Rights Reserved. This file may be used under the terms of the Be Sample Code License. */ -/* multiple-reader single-writer locking class */ +/** multiple-reader single-writer locking class */ + +// IMPORTANT: +// * nested read locks are not supported +// * a reader becomming the write is not supported +// * nested write locks are supported +// * a writer can do read locks, even nested ones +// * in case of problems, #define DEBUG 1 in the .cpp #ifndef MULTI_LOCKER_H #define MULTI_LOCKER_H @@ -31,7 +41,8 @@ class MultiLocker { bool WriteUnlock(); //does the current thread hold a write lock ? - bool IsWriteLocked(uint32 *stack_base = NULL, thread_id *thread = NULL); + bool IsWriteLocked(uint32 *stack_base = NULL, + thread_id *thread = NULL); //in DEBUG mode returns whether the lock is held //in non-debug mode returns true bool IsReadLocked(); @@ -82,31 +93,41 @@ class MultiLocker { class AutoWriteLocker { public: AutoWriteLocker(MultiLocker* lock) + : fLock(*lock) + { + fLock.WriteLock(); + } + AutoWriteLocker(MultiLocker& lock) : fLock(lock) { - fLock->WriteLock(); + fLock.WriteLock(); } ~AutoWriteLocker() { - fLock->WriteUnlock(); + fLock.WriteUnlock(); } private: - MultiLocker* fLock; + MultiLocker& fLock; }; class AutoReadLocker { public: AutoReadLocker(MultiLocker* lock) + : fLock(*lock) + { + fLock.ReadLock(); + } + AutoReadLocker(MultiLocker& lock) : fLock(lock) { - fLock->ReadLock(); + fLock.ReadLock(); } ~AutoReadLocker() { - fLock->ReadUnlock(); + fLock.ReadUnlock(); } private: - MultiLocker* fLock; + MultiLocker& fLock; }; diff --git a/src/servers/app/RegionPool.cpp b/src/servers/app/RegionPool.cpp new file mode 100644 index 0000000000..5746ef0967 --- /dev/null +++ b/src/servers/app/RegionPool.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2006, Haiku, Inc. + * Distributed under the terms of the MIT license. + * + * Authors: + * Stephan Aßmus + */ + +#include "RegionPool.h" + +#include +#include + +#if DEBUG_LEAK +#include +#endif + +#include + +using std::nothrow; + +RegionPool::RegionPool() + : fAvailable(4) +#if DEBUG_LEAK + ,fUsed(4) +#endif +{ +} + + +RegionPool::~RegionPool() +{ +#if DEBUG_LEAK + if (fUsed.CountItems() > 0) + debugger("RegionPool::~RegionPool() - some regions still in use!"); +#endif + int32 count = fAvailable.CountItems(); + for (int32 i = 0; i < count; i++) + delete (BRegion*)fAvailable.ItemAtFast(i); +} + + +BRegion* +RegionPool::GetRegion() +{ + BRegion* region = (BRegion*)fAvailable.RemoveItem( + fAvailable.CountItems() - 1); + if (!region) { + region = new (nothrow) BRegion(); + if (!region) { + // whoa + fprintf(stderr, "RegionPool::GetRegion() - " + "no memory!\n"); + } + } +#if DEBUG_LEAK + fUsed.AddItem(region); +#endif + return region; +} + + +BRegion* +RegionPool::GetRegion(const BRegion& other) +{ + BRegion* region; + int32 count = fAvailable.CountItems(); + if (count > 0) { + region = (BRegion*)fAvailable.RemoveItem(count - 1); + *region = other; + } else { + region = new (nothrow) BRegion(other); + if (!region) { + // whoa + fprintf(stderr, "RegionPool::GetRegion() - " + "no memory!\n"); + } + } + +#if DEBUG_LEAK + fUsed.AddItem(region); +#endif + return region; +} + + +void +RegionPool::Recycle(BRegion* region) +{ + if (!fAvailable.AddItem(region)) { + // at least don't leak the region... + fprintf(stderr, "RegionPool::Recycle() - " + "no memory!\n"); + delete region; + } else { + // prepare for next usage + region->MakeEmpty(); + } +#if DEBUG_LEAK + fUsed.RemoveItem(region); +#endif +} + diff --git a/src/servers/app/RegionPool.h b/src/servers/app/RegionPool.h new file mode 100644 index 0000000000..3457c4576a --- /dev/null +++ b/src/servers/app/RegionPool.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006, Haiku, Inc. + * Distributed under the terms of the MIT license. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef REGION_POOL_H +#define REGION_POOL_H + +#include + +class BRegion; + +#define DEBUG_LEAK 0 + +class RegionPool { + public: + RegionPool(); + virtual ~RegionPool(); + + BRegion* GetRegion(); + BRegion* GetRegion(const BRegion& other); + void Recycle(BRegion* region); + + private: + BList fAvailable; +#if DEBUG_LEAK + BList fUsed; +#endif +}; + +#endif // REGION_POOL_H diff --git a/src/servers/app/ServerBitmap.cpp b/src/servers/app/ServerBitmap.cpp index 381c3cc281..d96b560b21 100644 --- a/src/servers/app/ServerBitmap.cpp +++ b/src/servers/app/ServerBitmap.cpp @@ -194,6 +194,7 @@ ServerBitmap::_HandleSpace(color_space space, int32 bytesPerRow) case B_YCbCr422: case B_YUV422: minBPR = (fWidth + 3) / 4 * 8; + // TODO: huh? why not simply fWidth * 2 ?!? fBitsPerPixel = 16; break; diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index 3bf1be43ac..461696f383 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -51,7 +51,6 @@ #include "ServerPicture.h" #include "ServerProtocol.h" #include "WindowLayer.h" -#include "Workspace.h" #include "WorkspacesLayer.h" #include "ServerWindow.h" @@ -386,8 +385,11 @@ ServerWindow::SetTitle(const char* newTitle) rename_thread(Thread(), name); } - if (fWindowLayer != NULL) + if (fWindowLayer != NULL) { +fDesktop->UnlockSingleWindow(); fDesktop->SetWindowTitle(fWindowLayer, newTitle); +fDesktop->LockSingleWindow(); + } } @@ -584,10 +586,12 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) link.Read(&activate); +fDesktop->UnlockSingleWindow(); if (activate) fDesktop->ActivateWindow(fWindowLayer); else fDesktop->SendWindowBehind(fWindowLayer, NULL); +fDesktop->LockSingleWindow(); break; } case AS_SEND_BEHIND: @@ -602,7 +606,9 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) WindowLayer *behindOf; if ((behindOf = fDesktop->FindWindowLayerByClientToken(token, teamID)) != NULL) { +fDesktop->UnlockSingleWindow(); fDesktop->SendWindowBehind(fWindowLayer, behindOf); +fDesktop->LockSingleWindow(); status = B_OK; } else status = B_NAME_NOT_FOUND; @@ -617,23 +623,6 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) NotifyQuitRequested(); break; - case AS_BEGIN_TRANSACTION: - { - STRACE(("ServerWindow %s: Message AS_BEGIN_TRANSACTION unimplemented\n", - Title())); - // TODO: we could probably do a bit more here... -// TODO: AS_BEGIN_TRANSACTION - //fWindowLayer->DisableUpdateRequests(); - break; - } - case AS_END_TRANSACTION: - { - STRACE(("ServerWindow %s: Message AS_END_TRANSACTION unimplemented\n", - Title())); -// TODO: AS_END_TRANSACTION - //fWindowLayer->EnableUpdateRequests(); - break; - } case AS_ENABLE_UPDATES: { STRACE(("ServerWindow %s: Message AS_ENABLE_UPDATES unimplemented\n", @@ -683,8 +672,10 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) || windowLayer->Feel() != B_NORMAL_WINDOW_FEEL) { status = B_BAD_VALUE; } else { +fDesktop->UnlockSingleWindow(); status = fDesktop->AddWindowToSubset(fWindowLayer, windowLayer) ? B_OK : B_NO_MEMORY; +fDesktop->LockSingleWindow(); } } @@ -702,7 +693,9 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) WindowLayer* windowLayer = fDesktop->FindWindowLayerByClientToken( token, App()->ClientTeam()); if (windowLayer != NULL) { +fDesktop->UnlockSingleWindow(); fDesktop->RemoveWindowFromSubset(fWindowLayer, windowLayer); +fDesktop->LockSingleWindow(); status = B_OK; } else status = B_BAD_VALUE; @@ -725,8 +718,11 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) ? B_OK : B_BAD_VALUE; } - if (status == B_OK && !fWindowLayer->IsOffscreenWindow()) + if (status == B_OK && !fWindowLayer->IsOffscreenWindow()) { +fDesktop->UnlockSingleWindow(); fDesktop->SetWindowLook(fWindowLayer, (window_look)look); +fDesktop->LockSingleWindow(); + } fLink.StartMessage(status); fLink.Flush(); @@ -744,8 +740,11 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) ? B_OK : B_BAD_VALUE; } - if (status == B_OK && !fWindowLayer->IsOffscreenWindow()) + if (status == B_OK && !fWindowLayer->IsOffscreenWindow()) { +fDesktop->UnlockSingleWindow(); fDesktop->SetWindowFeel(fWindowLayer, (window_feel)feel); +fDesktop->LockSingleWindow(); + } fLink.StartMessage(status); fLink.Flush(); @@ -763,8 +762,11 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) ? B_OK : B_BAD_VALUE; } - if (status == B_OK && !fWindowLayer->IsOffscreenWindow()) + if (status == B_OK && !fWindowLayer->IsOffscreenWindow()) { +fDesktop->UnlockSingleWindow(); fDesktop->SetWindowFlags(fWindowLayer, flags); +fDesktop->LockSingleWindow(); + } fLink.StartMessage(status); fLink.Flush(); @@ -801,7 +803,9 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) STRACE(("ServerWindow %s: Message AS_SET_WORKSPACES %lx\n", Title(), newWorkspaces)); +fDesktop->UnlockSingleWindow(); fDesktop->SetWindowWorkspaces(fWindowLayer, newWorkspaces); +fDesktop->LockSingleWindow(); break; } case AS_WINDOW_RESIZE: @@ -820,7 +824,9 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) // pragmatically set window bounds fLink.StartMessage(B_BUSY); } else { +fDesktop->UnlockSingleWindow(); fDesktop->ResizeWindowBy(fWindowLayer, xResizeBy, yResizeBy); +fDesktop->LockSingleWindow(); fLink.StartMessage(B_OK); } fLink.Flush(); @@ -842,7 +848,9 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) // pragmatically set window positions fLink.StartMessage(B_BUSY); } else { +fDesktop->UnlockSingleWindow(); fDesktop->MoveWindowBy(fWindowLayer, xMoveBy, yMoveBy); +fDesktop->LockSingleWindow(); fLink.StartMessage(B_OK); } fLink.Flush(); @@ -901,22 +909,12 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) case AS_BEGIN_UPDATE: DTRACE(("ServerWindowo %s: AS_BEGIN_UPDATE\n", Title())); -// NOTE: when line below is turned on, the behaviour starts to -// be very much like on R5, but if the client crashes in -// one of it's BView's Draw() functions, AS_END_UPDATE is -// never received and the app_server is toast! Maybe R5 -// does something like this, but if the write lock cannot -// be optained within a certain amount of time, it crashes -// whoever holds the read lock on purpose. - -//fDesktop->LockSingleWindow(); fWindowLayer->BeginUpdate(fLink); break; case AS_END_UPDATE: DTRACE(("ServerWindowo %s: AS_END_UPDATE\n", Title())); fWindowLayer->EndUpdate(); -//fDesktop->UnlockSingleWindow(); break; case AS_GET_MOUSE: @@ -2697,7 +2695,6 @@ ServerWindow::_SetCurrentLayer(ViewLayer* layer) fCurrentDrawingRegionValid = false; #if 0 #if DELAYED_BACKGROUND_CLEARING -fWindowLayer->ReadLockWindows(); if (fCurrentLayer && fCurrentLayer->IsBackgroundDirty() && fWindowLayer->InUpdate()) { DrawingEngine* drawingEngine = fWindowLayer->GetDrawingEngine(); if (drawingEngine->Lock()) { @@ -2714,7 +2711,6 @@ fWindowLayer->ReadLockWindows(); drawingEngine->Unlock(); } } -fWindowLayer->ReadUnlockWindows(); #endif #endif // 0 } diff --git a/src/servers/app/ViewLayer.cpp b/src/servers/app/ViewLayer.cpp index 06c6f186ba..09479298a1 100644 --- a/src/servers/app/ViewLayer.cpp +++ b/src/servers/app/ViewLayer.cpp @@ -198,8 +198,12 @@ ViewLayer::AddChild(ViewLayer* layer) // trigger redraw BRect clippedFrame = layer->Frame(); ConvertToVisibleInTopView(&clippedFrame); - BRegion dirty(clippedFrame); - fWindow->MarkContentDirty(dirty); + BRegion* dirty = fWindow->GetRegion(); + if (dirty) { + dirty->Set(clippedFrame); + fWindow->MarkContentDirty(*dirty); + fWindow->RecycleRegion(dirty); + } } } } @@ -244,8 +248,12 @@ ViewLayer::RemoveChild(ViewLayer* layer) // trigger redraw BRect clippedFrame = layer->Frame(); ConvertToVisibleInTopView(&clippedFrame); - BRegion dirty(clippedFrame); - fWindow->MarkContentDirty(dirty); + BRegion* dirty = fWindow->GetRegion(); + if (dirty) { + dirty->Set(clippedFrame); + fWindow->MarkContentDirty(*dirty); + fWindow->RecycleRegion(dirty); + } } } @@ -642,13 +650,19 @@ ViewLayer::MoveBy(int32 x, int32 y, BRegion* dirtyRegion) // clipping oldVisibleBounds to newVisibleBounds // makes sure we don't copy parts hidden under // parent views - BRegion copyRegion(oldVisibleBounds & newVisibleBounds); - fWindow->CopyContents(©Region, x, y); + BRegion* region = fWindow->GetRegion(); + if (region) { + region->Set(oldVisibleBounds & newVisibleBounds); + fWindow->CopyContents(region, x, y); + + region->Set(oldVisibleBounds); + newVisibleBounds.OffsetBy(x, y); + region->Exclude(newVisibleBounds); + dirtyRegion->Include(dirty); + + fWindow->RecycleRegion(region); + } - BRegion dirty(oldVisibleBounds); - newVisibleBounds.OffsetBy(x, y); - dirty.Exclude(newVisibleBounds); - dirtyRegion->Include(&dirty); #endif } @@ -681,32 +695,37 @@ ViewLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion) oldBounds.right -= x; oldBounds.bottom -= y; - BRegion dirty(Bounds()); - dirty.Include(oldBounds); + BRegion* dirty = fWindow->GetRegion(); + if (!dirty) + return; + + dirty->Set(Bounds()); + dirty->Include(oldBounds); if (!(fFlags & B_FULL_UPDATE_ON_RESIZE)) { // the dirty region is just the difference of // old and new bounds - dirty.Exclude(oldBounds & Bounds()); + dirty->Exclude(oldBounds & Bounds()); } InvalidateScreenClipping(true); - if (dirty.CountRects() > 0) { + if (dirty->CountRects() > 0) { // exclude children, they are expected to // include their own dirty regions in ParentResized() for (ViewLayer* child = FirstChild(); child; child = child->NextSibling()) { if (child->IsVisible()) { BRect previousChildVisible(child->Frame() & oldBounds & Bounds()); - if (dirty.Frame().Intersects(previousChildVisible)) { - dirty.Exclude(previousChildVisible); + if (dirty->Frame().Intersects(previousChildVisible)) { + dirty->Exclude(previousChildVisible); } } } - ConvertToScreen(&dirty); - dirtyRegion->Include(&dirty); + ConvertToScreen(dirty); + dirtyRegion->Include(dirty); } + fWindow->RecycleRegion(dirty); } // layout the children @@ -795,10 +814,16 @@ ViewLayer::ScrollBy(int32 x, int32 y, BRegion* dirtyRegion) // find the dirty region as far as we are // concerned - BRegion dirty(oldBounds); + BRegion* dirty = fWindow->GetRegion(); + if (!dirty) + return; + + dirty->Set(oldBounds); stillVisibleBounds.OffsetBy(-x, -y); - dirty.Exclude(stillVisibleBounds); - dirtyRegion->Include(&dirty); + dirty->Exclude(stillVisibleBounds); + dirtyRegion->Include(dirty); + + fWindow->RecycleRegion(dirty); // the screen clipping of this view and it's // childs is no longer valid @@ -843,12 +868,18 @@ ViewLayer::CopyBits(BRect src, BRect dst, BRegion& windowContentClipping) BRect dirtyDst(dst); ConvertToVisibleInTopView(&dirtyDst); - BRegion dirty(dirtyDst); + BRegion* dirty = fWindow->GetRegion(); + if (!dirty) + return; + + dirty->Set(dirtyDst); // exclude the part that we could copy visibleSrc.OffsetBy(xOffset, yOffset); - dirty.Exclude(visibleSrc); - dirty.IntersectWith(&ScreenClipping(&windowContentClipping)); - fWindow->MarkContentDirty(dirty); + dirty->Exclude(visibleSrc); + dirty->IntersectWith(&ScreenClipping(&windowContentClipping)); + fWindow->MarkContentDirty(*dirty); + + fWindow->RecycleRegion(dirty); } @@ -1024,8 +1055,12 @@ ViewLayer::SetHidden(bool hidden) // trigger a redraw BRect clippedBounds = Bounds(); ConvertToVisibleInTopView(&clippedBounds); - BRegion dirty(clippedBounds); - fWindow->MarkContentDirty(dirty); + BRegion* dirty = fWindow->GetRegion(); + if (!dirty) + return; + dirty->Set(clippedBounds); + fWindow->MarkContentDirty(*dirty); + fWindow->RecycleRegion(dirty); } } } else { diff --git a/src/servers/app/WindowLayer.cpp b/src/servers/app/WindowLayer.cpp index f8014b2477..160099d3cf 100644 --- a/src/servers/app/WindowLayer.cpp +++ b/src/servers/app/WindowLayer.cpp @@ -88,6 +88,8 @@ WindowLayer::WindowLayer(const BRect& frame, const char *name, fEffectiveDrawingRegion(), fEffectiveDrawingRegionValid(false), + fRegionPool(), + fIsClosing(false), fIsMinimizing(false), fIsZooming(false), @@ -122,9 +124,7 @@ WindowLayer::WindowLayer(const BRect& frame, const char *name, fMinWidth(1), fMaxWidth(32768), fMinHeight(1), - fMaxHeight(32768), - - fReadLocked(false) + fMaxHeight(32768) { // make sure our arguments are valid if (!IsValidLook(fLook)) @@ -175,29 +175,6 @@ WindowLayer::~WindowLayer() } -bool -WindowLayer::ReadLockWindows() -{ - if (fDesktop) - fReadLocked = fDesktop->LockSingleWindow(); - else - fReadLocked = true; - - return fReadLocked; -} - - -void -WindowLayer::ReadUnlockWindows() -{ - if (fReadLocked) { - if (fDesktop) - fDesktop->UnlockSingleWindow(); - fReadLocked = false; - } -} - - void WindowLayer::SetClipping(BRegion* stillAvailableOnScreen) { @@ -413,29 +390,27 @@ WindowLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion) void WindowLayer::ScrollViewBy(ViewLayer* view, int32 dx, int32 dy) { - // this can be executed from any thread, but if the - // desktop thread is executing this, it should have - // the write lock, otherwise it is not prevented - // from executing this at the same time as the window - // is doing something else here! + // this is executed in ServerWindow with the Readlock + // held if (!view || view == fTopLayer || (dx == 0 && dy == 0)) return; - if (fDesktop && fDesktop->LockSingleWindow()) { - BRegion dirty; - view->ScrollBy(dx, dy, &dirty); + BRegion* dirty = fRegionPool.GetRegion(); + if (!dirty) + return; + + view->ScrollBy(dx, dy, dirty); //fDrawingEngine->FillRegion(dirty, RGBColor(255, 0, 255, 255)); //snooze(2000); - if (IsVisible() && view->IsVisible()) { - dirty.IntersectWith(&VisibleContentRegion()); - _TriggerContentRedraw(dirty); - } - - fDesktop->UnlockSingleWindow(); + if (IsVisible() && view->IsVisible()) { + dirty->IntersectWith(&VisibleContentRegion()); + _TriggerContentRedraw(*dirty); } + + fRegionPool.Recycle(dirty); } @@ -443,64 +418,67 @@ WindowLayer::ScrollViewBy(ViewLayer* view, int32 dx, int32 dy) void WindowLayer::CopyContents(BRegion* region, int32 xOffset, int32 yOffset) { - if (IsVisible() && fDesktop && fDesktop->LockSingleWindow()) { - BRegion newDirty(*region); + // executed in ServerWindow thread with the read lock held + if (!IsVisible()) + return; - // clip the region to the visible contents at the - // source and destination location (note that VisibleContentRegion() - // is used once to make sure it is valid, then fVisibleContentRegion - // is used directly) - region->IntersectWith(&VisibleContentRegion()); + BRegion* newDirty = fRegionPool.GetRegion(*region); + + // clip the region to the visible contents at the + // source and destination location (note that VisibleContentRegion() + // is used once to make sure it is valid, then fVisibleContentRegion + // is used directly) + region->IntersectWith(&VisibleContentRegion()); + if (region->CountRects() > 0) { + region->OffsetBy(xOffset, yOffset); + region->IntersectWith(&fVisibleContentRegion); if (region->CountRects() > 0) { - region->OffsetBy(xOffset, yOffset); - region->IntersectWith(&fVisibleContentRegion); - if (region->CountRects() > 0) { - // if the region still contains any rects - // offset to source location again - region->OffsetBy(-xOffset, -yOffset); - // the part which we can copy is not dirty - newDirty.Exclude(region); - - fDrawingEngine->CopyRegion(region, xOffset, yOffset); + // if the region still contains any rects + // offset to source location again + region->OffsetBy(-xOffset, -yOffset); + // the part which we can copy is not dirty + newDirty->Exclude(region); + + fDrawingEngine->CopyRegion(region, xOffset, yOffset); - // move along the already dirty regions that are common - // with the region that we could copy - _ShiftPartOfRegion(&fDirtyRegion, region, xOffset, yOffset); - if (fPendingUpdateSession.IsUsed()) - _ShiftPartOfRegion(&fPendingUpdateSession.DirtyRegion(), region, xOffset, yOffset); + // move along the already dirty regions that are common + // with the region that we could copy + _ShiftPartOfRegion(&fDirtyRegion, region, xOffset, yOffset); + if (fPendingUpdateSession.IsUsed()) + _ShiftPartOfRegion(&fPendingUpdateSession.DirtyRegion(), region, xOffset, yOffset); - if (fCurrentUpdateSession.IsUsed()) { - // if there are parts in the current update session - // that intersect with the copied region, we cannot - // simply shift them as with the other dirty regions - // - we cannot change the update rect already told to the - // client, that's why we transfer those parts to the - // new dirty region instead - BRegion common(*region); - // see if there is a common part at all - common.IntersectWith(&fCurrentUpdateSession.DirtyRegion()); - if (common.CountRects() > 0) { - // cut the common part from the region - fCurrentUpdateSession.DirtyRegion().Exclude(&common); - newDirty.Include(&common); - } + if (fCurrentUpdateSession.IsUsed()) { + // if there are parts in the current update session + // that intersect with the copied region, we cannot + // simply shift them as with the other dirty regions + // - we cannot change the update rect already told to the + // client, that's why we transfer those parts to the + // new dirty region instead + BRegion* common = fRegionPool.GetRegion(*region); + // see if there is a common part at all + common->IntersectWith(&fCurrentUpdateSession.DirtyRegion()); + if (common->CountRects() > 0) { + // cut the common part from the region + fCurrentUpdateSession.DirtyRegion().Exclude(common); + newDirty->Include(common); } + fRegionPool.Recycle(common); } } - // what is left visible from the original region - // at the destination after the region which could be - // copied has been excluded, is considered dirty - // NOTE: it may look like dirty regions are not moved - // if no region could be copied, but that's alright, - // since these parts will now be in newDirty anyways - // (with the right offset) - newDirty.OffsetBy(xOffset, yOffset); - newDirty.IntersectWith(&fVisibleContentRegion); - if (newDirty.CountRects() > 0) - ProcessDirtyRegion(newDirty); - - fDesktop->UnlockSingleWindow(); } + // what is left visible from the original region + // at the destination after the region which could be + // copied has been excluded, is considered dirty + // NOTE: it may look like dirty regions are not moved + // if no region could be copied, but that's alright, + // since these parts will now be in newDirty anyways + // (with the right offset) + newDirty->OffsetBy(xOffset, yOffset); + newDirty->IntersectWith(&fVisibleContentRegion); + if (newDirty->CountRects() > 0) + ProcessDirtyRegion(*newDirty); + + fRegionPool.Recycle(newDirty); } @@ -537,14 +515,11 @@ WindowLayer::ViewAt(const BPoint& where) { ViewLayer* view = NULL; - if (ReadLockWindows()) { - if (!fContentRegionValid) - _UpdateContentRegion(); + if (!fContentRegionValid) + _UpdateContentRegion(); - view = fTopLayer->ViewAt(where, &fContentRegion); + view = fTopLayer->ViewAt(where, &fContentRegion); - ReadUnlockWindows(); - } return view; } @@ -641,16 +616,18 @@ WindowLayer::ProcessDirtyRegion(BRegion& region) void WindowLayer::RedrawDirtyRegion() { - if (!fDesktop || !fDesktop->LockSingleWindow()) - return; + // executed from ServerWindow with the read lock held if (IsVisible()) { _DrawBorder(); - BRegion dirtyContentRegion(VisibleContentRegion()); - dirtyContentRegion.IntersectWith(&fDirtyRegion); + BRegion* dirtyContentRegion = + fRegionPool.GetRegion(VisibleContentRegion()); + dirtyContentRegion->IntersectWith(&fDirtyRegion); - _TriggerContentRedraw(dirtyContentRegion); + _TriggerContentRedraw(*dirtyContentRegion); + + fRegionPool.Recycle(dirtyContentRegion); } // reset the dirty region, since @@ -658,11 +635,9 @@ WindowLayer::RedrawDirtyRegion() // thread wanted to mark something // dirty in the mean time, it was // blocking on the global region lock to - // get write access, since we held the - // read lock for the whole time. + // get write access, since we're holding + // the read lock for the whole time. fDirtyRegion.MakeEmpty(); - - fDesktop->UnlockSingleWindow(); } @@ -685,23 +660,19 @@ WindowLayer::MarkContentDirty(BRegion& regionOnScreen) // since this won't affect other windows, read locking // is sufficient. If there was no dirty region before, // an update message is triggered - if (!fHidden && fDesktop && fDesktop->LockSingleWindow()) { - regionOnScreen.IntersectWith(&VisibleContentRegion()); - _TriggerContentRedraw(regionOnScreen); + if (fHidden) + return; - fDesktop->UnlockSingleWindow(); - } + regionOnScreen.IntersectWith(&VisibleContentRegion()); + _TriggerContentRedraw(regionOnScreen); } void WindowLayer::InvalidateView(ViewLayer* layer, BRegion& layerRegion) { - if (layer && IsVisible() && fDesktop && fDesktop->LockSingleWindow()) { - if (!layer->IsVisible()) { - fDesktop->UnlockSingleWindow(); - return; - } + if (layer && IsVisible() && layer->IsVisible()) { + if (!fContentRegionValid) _UpdateContentRegion(); @@ -715,8 +686,6 @@ WindowLayer::InvalidateView(ViewLayer* layer, BRegion& layerRegion) _TriggerContentRedraw(layerRegion); } - - fDesktop->UnlockSingleWindow(); } } @@ -796,12 +765,12 @@ WindowLayer::MouseDown(BMessage* message, BPoint where, int32* _viewToken) } // redraw decorator - BRegion visibleBorder; - GetBorderRegion(&visibleBorder); - visibleBorder.IntersectWith(&VisibleRegion()); + BRegion* visibleBorder = fRegionPool.GetRegion(); + GetBorderRegion(visibleBorder); + visibleBorder->IntersectWith(&VisibleRegion()); fDrawingEngine->Lock(); - fDrawingEngine->ConstrainClippingRegion(&visibleBorder); + fDrawingEngine->ConstrainClippingRegion(visibleBorder); if (fIsZooming) { fDecorator->SetZoom(true); @@ -813,6 +782,8 @@ WindowLayer::MouseDown(BMessage* message, BPoint where, int32* _viewToken) fDrawingEngine->Unlock(); + fRegionPool.Recycle(visibleBorder); + // based on what the Decorator returned, properly place this window. if (action == DEC_MOVETOBACK) { fDesktop->SendWindowBehind(this); @@ -853,12 +824,12 @@ WindowLayer::MouseUp(BMessage* message, BPoint where, int32* _viewToken) click_type action = _ActionFor(message); // redraw decorator - BRegion visibleBorder; - GetBorderRegion(&visibleBorder); - visibleBorder.IntersectWith(&VisibleRegion()); + BRegion* visibleBorder = fRegionPool.GetRegion(); + GetBorderRegion(visibleBorder); + visibleBorder->IntersectWith(&VisibleRegion()); fDrawingEngine->Lock(); - fDrawingEngine->ConstrainClippingRegion(&visibleBorder); + fDrawingEngine->ConstrainClippingRegion(visibleBorder); if (fIsZooming) { fIsZooming = false; @@ -886,6 +857,8 @@ WindowLayer::MouseUp(BMessage* message, BPoint where, int32* _viewToken) } fDrawingEngine->Unlock(); + + fRegionPool.Recycle(visibleBorder); } fIsDragging = false; fIsResizing = false; @@ -923,12 +896,12 @@ WindowLayer::MouseMoved(BMessage *message, BPoint where, int32* _viewToken, } if (fDecorator) { - BRegion visibleBorder; - GetBorderRegion(&visibleBorder); - visibleBorder.IntersectWith(&VisibleRegion()); + BRegion* visibleBorder = fRegionPool.GetRegion(); + GetBorderRegion(visibleBorder); + visibleBorder->IntersectWith(&VisibleRegion()); fDrawingEngine->Lock(); - fDrawingEngine->ConstrainClippingRegion(&visibleBorder); + fDrawingEngine->ConstrainClippingRegion(visibleBorder); if (fIsZooming) { fDecorator->SetZoom(_ActionFor(message) == DEC_ZOOM); @@ -939,6 +912,7 @@ WindowLayer::MouseMoved(BMessage *message, BPoint where, int32* _viewToken, } fDrawingEngine->Unlock(); + fRegionPool.Recycle(visibleBorder); } BPoint delta = where - fLastMousePosition; @@ -1071,9 +1045,12 @@ WindowLayer::SetFocus(bool focus) // so the window thread cannot be // accessing fIsFocus - BRegion dirty(fBorderRegion); - dirty.IntersectWith(&fVisibleRegion); - fDesktop->MarkDirty(dirty); + BRegion* dirty = fRegionPool.GetRegion(fBorderRegion); + if (dirty) { + dirty->IntersectWith(&fVisibleRegion); + fDesktop->MarkDirty(*dirty); + fRegionPool.Recycle(dirty); + } fIsFocus = focus; if (fDecorator) @@ -1602,16 +1579,19 @@ void WindowLayer::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift, int32 xOffset, int32 yOffset) { - BRegion common(*regionToShift); + BRegion* common = fRegionPool.GetRegion(*regionToShift); + if (!common) + return; // see if there is a common part at all - common.IntersectWith(region); - if (common.CountRects() > 0) { + common->IntersectWith(region); + if (common->CountRects() > 0) { // cut the common part from the region, // offset that to destination and include again - region->Exclude(&common); - common.OffsetBy(xOffset, yOffset); - region->Include(&common); + region->Exclude(common); + common->OffsetBy(xOffset, yOffset); + region->Include(common); } + fRegionPool.Recycle(common); } @@ -1655,19 +1635,22 @@ WindowLayer::_DrawBorder() return; // construct the region of the border that needs redrawing - BRegion dirtyBorderRegion; - GetBorderRegion(&dirtyBorderRegion); + BRegion* dirtyBorderRegion = fRegionPool.GetRegion(); + if (!dirtyBorderRegion) + return; + GetBorderRegion(dirtyBorderRegion); // intersect with our visible region - dirtyBorderRegion.IntersectWith(&fVisibleRegion); + dirtyBorderRegion->IntersectWith(&fVisibleRegion); // intersect with the dirty region - dirtyBorderRegion.IntersectWith(&fDirtyRegion); + dirtyBorderRegion->IntersectWith(&fDirtyRegion); - if (dirtyBorderRegion.CountRects() > 0 && fDrawingEngine->Lock()) { - fDrawingEngine->ConstrainClippingRegion(&dirtyBorderRegion); - fDecorator->Draw(dirtyBorderRegion.Frame()); + if (dirtyBorderRegion->CountRects() > 0 && fDrawingEngine->Lock()) { + fDrawingEngine->ConstrainClippingRegion(dirtyBorderRegion); + fDecorator->Draw(dirtyBorderRegion->Frame()); fDrawingEngine->Unlock(); } + fRegionPool.Recycle(dirtyBorderRegion); } @@ -1730,15 +1713,10 @@ WindowLayer::BeginUpdate(BPrivate::PortLink& link) // NOTE: since we might "shift" parts of the // internal dirty regions from the desktop thread // in response to WindowLayer::ResizeBy(), which - // might move arround views, this function needs to block - // on the global clipping lock so that the internal + // might move arround views, the user of this function + // needs to hold the global clipping lock so that the internal // dirty regions are not messed with from the Desktop thread // and ServerWindow thread at the same time. - if (!fDesktop->LockSingleWindow()) { - link.StartMessage(B_ERROR); - link.Flush(); - return; - } if (fUpdateRequested) { // make the pending update session the current update session @@ -1760,8 +1738,15 @@ WindowLayer::BeginUpdate(BPrivate::PortLink& link) if (!fContentRegionValid) _UpdateContentRegion(); - BRegion dirty(fCurrentUpdateSession.DirtyRegion()); - dirty.IntersectWith(&VisibleContentRegion()); + BRegion* dirty = fRegionPool.GetRegion( + fCurrentUpdateSession.DirtyRegion()); + if (!dirty) { + link.StartMessage(B_ERROR); + link.Flush(); + return; + } + + dirty->IntersectWith(&VisibleContentRegion()); //fDrawingEngine->FillRegion(dirty, RGBColor(255, 0, 0, 255)); @@ -1772,10 +1757,10 @@ WindowLayer::BeginUpdate(BPrivate::PortLink& link) link.Attach(fFrame.Width()); link.Attach(fFrame.Height()); // append he update rect in screen coords - link.Attach(dirty.Frame()); + link.Attach(dirty->Frame()); // find and attach all views that intersect with // the dirty region - fTopLayer->AddTokensForLayersInRegion(link, dirty, &fContentRegion); + fTopLayer->AddTokensForLayersInRegion(link, *dirty, &fContentRegion); // mark the end of the token "list" link.Attach(B_NULL_TOKEN); link.Flush(); @@ -1783,17 +1768,17 @@ WindowLayer::BeginUpdate(BPrivate::PortLink& link) #if DELAYED_BACKGROUND_CLEARING // NOTE: turning off DELAYED_BACKGROUND_CLEARING will // need investigation if it even still works... - fTopLayer->Draw(fDrawingEngine, &dirty, + fTopLayer->Draw(fDrawingEngine, dirty, &fContentRegion, true); -#endif + + fRegionPool.Recycle(dirty); +#endif } else { printf("BeginUpdate() but no update requested!!\n"); link.StartMessage(B_ERROR); link.Flush(); fprintf(stderr, "WindowLayer::BeginUpdate() - no update requested!\n"); } - - fDesktop->UnlockSingleWindow(); } @@ -1801,8 +1786,6 @@ void WindowLayer::EndUpdate() { // NOTE: see comment in _BeginUpdate() - if (!fDesktop->LockSingleWindow()) - return; if (fInUpdate) { fCurrentUpdateSession.SetUsed(false); @@ -1816,8 +1799,6 @@ WindowLayer::EndUpdate() } else { fUpdateRequested = false; } - - fDesktop->UnlockSingleWindow(); } diff --git a/src/servers/app/WindowLayer.h b/src/servers/app/WindowLayer.h index 86df8801b7..cc67133d42 100644 --- a/src/servers/app/WindowLayer.h +++ b/src/servers/app/WindowLayer.h @@ -13,6 +13,7 @@ #include "Decorator.h" #include "ViewLayer.h" +#include "RegionPool.h" #include "ServerWindow.h" #include "WindowList.h" @@ -56,10 +57,6 @@ class WindowLayer { ::ServerWindow* ServerWindow() const { return fWindow; } ::EventTarget& EventTarget() const { return fWindow->EventTarget(); } - // for convenience, handles window not attached to Desktop - bool ReadLockWindows(); - void ReadUnlockWindows(); - // setting and getting the "hard" clipping, you need to have // WriteLock()ed the clipping! void SetClipping(BRegion* stillAvailableOnScreen); @@ -116,6 +113,16 @@ class WindowLayer { DrawingEngine* GetDrawingEngine() const { return fDrawingEngine; } + // managing a region pool + ::RegionPool* RegionPool() + { return &fRegionPool; } + inline BRegion* GetRegion() + { return fRegionPool.GetRegion(); } + inline BRegion* GetRegion(const BRegion& copy) + { return fRegionPool.GetRegion(copy); } + inline void RecycleRegion(BRegion* region) + { fRegionPool.Recycle(region); } + void CopyContents(BRegion* region, int32 xOffset, int32 yOffset); @@ -248,6 +255,8 @@ class WindowLayer { BRegion fEffectiveDrawingRegion; bool fEffectiveDrawingRegionValid; + ::RegionPool fRegionPool; + BObjectList fSubsets; // TODO: remove those some day (let the decorator handle that stuff) @@ -322,8 +331,6 @@ class WindowLayer { int32 fMaxWidth; int32 fMinHeight; int32 fMaxHeight; - - mutable bool fReadLocked; }; #endif // WINDOW_LAYER_H diff --git a/src/servers/app/Workspace.cpp b/src/servers/app/Workspace.cpp index c58485a54a..30d814c244 100644 --- a/src/servers/app/Workspace.cpp +++ b/src/servers/app/Workspace.cpp @@ -77,14 +77,18 @@ Workspace::Workspace(Desktop& desktop, int32 index) fDesktop(desktop), fCurrentWorkspace(index == desktop.CurrentWorkspace()) { - fDesktop.LockSingleWindow(); +// fDesktop.LockSingleWindow(); + // TODO: in which threads is this being used? + // from my investigations, it is used in the + // WorkspacesLayer::Draw(), which would have + // to hold the read lock already RewindWindows(); } Workspace::~Workspace() { - fDesktop.UnlockSingleWindow(); +// fDesktop.UnlockSingleWindow(); } diff --git a/src/tests/servers/app/Jamfile b/src/tests/servers/app/Jamfile index 84d73e0201..bc443bd5db 100644 --- a/src/tests/servers/app/Jamfile +++ b/src/tests/servers/app/Jamfile @@ -20,8 +20,8 @@ UseFreeTypeHeaders ; local defines = [ FDefines TEST_MODE=1 ] ; # USE_DIRECT_WINDOW_TEST_MODE=1 -SubDirCcFlags $(defines) -fcheck-memory-usage -D_NO_INLINE_ASM ; -SubDirC++Flags $(defines) -fcheck-memory-usage -D_NO_INLINE_ASM ; +SubDirCcFlags $(defines) ; #-fcheck-memory-usage -D_NO_INLINE_ASM ; +SubDirC++Flags $(defines) ; #-fcheck-memory-usage -D_NO_INLINE_ASM ; SEARCH_SOURCE += $(appServerDir) [ FDirName $(appServerDir) drawing ] ; @@ -112,6 +112,7 @@ Server haiku_app_server : DefaultDecorator.cpp OffscreenServerWindow.cpp OffscreenWindowLayer.cpp + RegionPool.cpp ServerPicture.cpp ServerScreen.cpp ViewLayer.cpp