Reworked the handling of periodically updated poses (currently only ones with a volume space bar):

* Addad global list where poses that need periodic updates can be registered with a callback
* Use this mechanism for poses with a volume space bar
* Create only one BVolume when the BPose is created for a volume, instead of every time the free space is calculated
* On Pulse() the global list is used to update all of the registered periodic update poses
* As the poses know their volume, it is no longer necessary to use a BVolumeRoster to loop through each volume on each Pulse()
* Removed the now superfluous SendNotices() mechanism
* Removed corresponding watching / handling of these notices in BPoseView

The BPoseView did a linear search for each volume pose on each Pulse() before. What's more it did this once for each mounted volume as it did get one individual notice for each of them. To get these volumes a BVolumeRoster was used to loop through the volumes, but then the BPose did still create a new BVolume to actually calculate the free space! I'm surprised that it did not suck away more performance with this method...

Anyway, this should bring down BVolume construction and update overhead down to a minimum and hopefully fix ticket #1247.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21462 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2007-06-19 09:33:29 +00:00
parent 5de78b2cdb
commit 687e327bac
10 changed files with 185 additions and 116 deletions

View File

@ -49,32 +49,15 @@ All rights reserved.
int32
CalcFreeSpace(dev_t device)
CalcFreeSpace(BVolume *volume)
{
BVolume volume(device);
fs_info info;
if (volume.InitCheck() == B_OK && fs_stat_dev(device,&info) == B_OK) {
// Philosophy here:
// Bars go on all drives with read/write capabilities
// Exceptions: Not on CDDA, but on NTFS/Ext2
// Also note that some volumes may return 0 when
// BVolume::Capacity() is called (believe-me... That *DOES*
// happen) so we also check for that.
off_t capacity = volume.Capacity();
if (((!volume.IsReadOnly() && strcmp(info.fsh_name,"cdda"))
|| !strcmp(info.fsh_name,"ntfs")
|| !strcmp(info.fsh_name,"ext2"))
&& (capacity > 0)) {
int32 percent = static_cast<int32>(volume.FreeBytes() / (capacity / 100));
off_t capacity = volume->Capacity();
int32 percent = static_cast<int32>(volume->FreeBytes() / (capacity / 100));
// warn below 20 MB of free space (if this is less than 10% of free space)
if (volume.FreeBytes() < 20 * 1024 * 1024 && percent < 10)
return -2 - percent;
return percent;
}
}
return -1;
// warn below 20 MB of free space (if this is less than 10% of free space)
if (volume->FreeBytes() < 20 * 1024 * 1024 && percent < 10)
return -2 - percent;
return percent;
}
@ -98,9 +81,32 @@ BPose::BPose(Model *model, BPoseView *view, bool selected)
{
CreateWidgets(view);
if (model->IsVolume() && TrackerSettings().ShowVolumeSpaceBar()) {
if (model->IsVolume()) {
fs_info info;
dev_t device = model->NodeRef()->device;
fPercent = CalcFreeSpace(device);
BVolume *volume = new BVolume(device);
if (volume->InitCheck() == B_OK
&& fs_stat_dev(device, &info) == B_OK) {
// Philosophy here:
// Bars go on all drives with read/write capabilities
// Exceptions: Not on CDDA, but on NTFS/Ext2
// Also note that some volumes may return 0 when
// BVolume::Capacity() is called (believe-me... That *DOES*
// happen) so we also check for that.
off_t capacity = volume->Capacity();
if (((!volume->IsReadOnly() && strcmp(info.fsh_name,"cdda"))
|| !strcmp(info.fsh_name,"ntfs")
|| !strcmp(info.fsh_name,"ext2"))
&& capacity > 0) {
// The volume is ok and we want space bars on it
gPeriodicUpdatePoses.AddPose(this, view,
_PeriodicUpdateCallback, volume);
if (TrackerSettings().ShowVolumeSpaceBar())
fPercent = CalcFreeSpace(volume);
} else
delete volume;
} else
delete volume;
}
if ((fClipboardMode = FSClipboardFindNodeMode(model,true)) != 0
@ -112,6 +118,13 @@ BPose::BPose(Model *model, BPoseView *view, bool selected)
BPose::~BPose()
{
if (fModel->IsVolume()) {
// we might be registered for periodic updates
BVolume *volume = NULL;
if (gPeriodicUpdatePoses.RemovePose(this, (void **)&volume))
delete volume;
}
delete fModel;
}
@ -285,8 +298,16 @@ BPose::UpdateWidgetAndModel(Model *resolvedModel, const char *attrName,
bool
BPose::UpdateVolumeSpaceBar(bool enabled)
BPose::_PeriodicUpdateCallback(BPose *pose, void *cookie)
{
return pose->UpdateVolumeSpaceBar((BVolume *)cookie);
}
bool
BPose::UpdateVolumeSpaceBar(BVolume *volume)
{
bool enabled = TrackerSettings().ShowVolumeSpaceBar();
if (!enabled) {
if (fPercent == -1)
return false;
@ -295,9 +316,7 @@ BPose::UpdateVolumeSpaceBar(bool enabled)
return true;
}
dev_t device = TargetModel()->NodeRef()->device;
int32 percent = CalcFreeSpace(device);
int32 percent = CalcFreeSpace(volume);
if (fPercent != percent) {
if (percent > 100)
fPercent = 100;

View File

@ -96,7 +96,7 @@ class BPose {
void UpdateAllWidgets(int32 poseIndex, BPoint poseLoc, BPoseView *);
void UpdateWidgetAndModel(Model *resolvedModel, const char *attrName,
uint32 attrType, int32 poseIndex, BPoint poseLoc, BPoseView *view);
bool UpdateVolumeSpaceBar(bool enabled);
bool UpdateVolumeSpaceBar(BVolume *volume);
void UpdateIcon(BPoint poseLoc, BPoseView *);
//void UpdateFixedSymlink(BPoint poseLoc, BPoseView *);
@ -126,6 +126,7 @@ class BPose {
#endif
private:
static bool _PeriodicUpdateCallback(BPose *pose, void *cookie);
void EditPreviousNextWidgetCommon(BPoseView *poseView, bool next);
void CreateWidgets(BPoseView *);
bool TestLargeIconPixel(BPoint) const;

View File

@ -105,20 +105,3 @@ PoseList::DeepFindPose(const node_ref *node, int32 *resultingIndex) const
return NULL;
}
BPose *
PoseList::FindVolumePose(const dev_t device, int32 *resultingIndex) const
{
int32 count = CountItems();
for (int32 index = 0; index < count; index++) {
BPose *pose = ItemAt(index);
Model *model = pose->TargetModel();
ASSERT(model);
if (model->IsVolume() && model->NodeRef()->device == device) {
if (resultingIndex)
*resultingIndex = index;
return pose;
}
}
return NULL;
}

View File

@ -64,7 +64,6 @@ public:
BPose *DeepFindPose(const node_ref *node, int32 *index = NULL) const;
// same as FindPose, node can be a target of the actual
// pose if the pose is a symlink
BPose *FindVolumePose(const dev_t device, int32 *index = NULL) const;
};
// iteration glue, add permutations as needed

View File

@ -803,9 +803,6 @@ BPoseView::DetachedFromWindow()
app->StopWatching(this, kShowSelectionWhenInactiveChanged);
app->StopWatching(this, kTransparentSelectionChanged);
app->StopWatching(this, kSortFolderNamesFirstChanged);
app->StopWatching(this, kShowVolumeSpaceBar);
app->StopWatching(this, kSpaceBarColorChanged);
app->StopWatching(this, kUpdateVolumeSpaceBar);
app->Unlock();
}
@ -890,9 +887,6 @@ BPoseView::AttachedToWindow()
app->StartWatching(this, kShowSelectionWhenInactiveChanged);
app->StartWatching(this, kTransparentSelectionChanged);
app->StartWatching(this, kSortFolderNamesFirstChanged);
app->StartWatching(this, kShowVolumeSpaceBar);
app->StartWatching(this, kSpaceBarColorChanged);
app->StartWatching(this, kUpdateVolumeSpaceBar);
app->Unlock();
}
@ -2331,20 +2325,6 @@ BPoseView::MessageReceived(BMessage *message)
Invalidate();
}
break;
case kShowVolumeSpaceBar:
bool enabled;
if (message->FindBool("ShowVolumeSpaceBar", &enabled) == B_OK)
TrackerSettings().SetShowVolumeSpaceBar(enabled);
// supposed to fall through
case kSpaceBarColorChanged:
UpdateVolumeIcons();
break;
case kUpdateVolumeSpaceBar:
dev_t device;
message->FindInt32("device", (int32 *)&device);
UpdateVolumeIcon(device);
break;
}
}
break;
@ -5214,34 +5194,21 @@ BPoseView::AttributeChanged(const BMessage *message)
void
BPoseView::UpdateVolumeIcon(dev_t device, bool forceUpdate)
BPoseView::UpdateIcon(BPose *pose)
{
int32 index;
BPose *pose = fPoseList->FindVolumePose(device,&index);
if (pose == NULL)
return;
if (pose->UpdateVolumeSpaceBar(TrackerSettings().ShowVolumeSpaceBar()) || forceUpdate) {
BPoint loc(0, index * fListElemHeight);
pose->UpdateIcon(loc, this);
BPoint location;
if (ViewMode() == kListMode) {
// need to find the index of the pose in the pose list
int32 count = fPoseList->CountItems();
for (int32 index = 0; index < count; index++) {
if (fPoseList->ItemAt(index) == pose) {
location.Set(0, index * fListElemHeight);
break;
}
}
}
}
void
BPoseView::UpdateVolumeIcons()
{
BVolumeRoster roster;
BVolume volume;
while(roster.GetNextVolume(&volume) == B_NO_ERROR) {
BDirectory dir;
volume.GetRootDirectory(&dir);
node_ref nodeRef;
dir.GetNodeRef(&nodeRef);
UpdateVolumeIcon(nodeRef.device, true);
}
pose->UpdateIcon(location, this);
}

View File

@ -159,8 +159,7 @@ class BPoseView : public BView {
void SetAutoScroll(bool);
void SetPoseEditing(bool);
void UpdateVolumeIcon(dev_t device, bool forceUpdate = false);
void UpdateVolumeIcons();
void UpdateIcon(BPose *pose);
// file change notification handler
virtual bool FSNotification(const BMessage *);

View File

@ -1072,9 +1072,7 @@ SpaceBarSettingsView::MessageReceived(BMessage *message)
{
settings.SetShowVolumeSpaceBar(fSpaceBarShowCheckBox->Value() == 1);
Window()->PostMessage(kSettingsContentsModified);
BMessage notificationMessage;
notificationMessage.AddBool("ShowVolumeSpaceBar", settings.ShowVolumeSpaceBar());
tracker->SendNotices(kShowVolumeSpaceBar, &notificationMessage);
tracker->PostMessage(kShowVolumeSpaceBar);
break;
}
@ -1094,6 +1092,7 @@ SpaceBarSettingsView::MessageReceived(BMessage *message)
}
break;
}
case kSpaceBarColorChanged:
{
switch (fCurrentColor) {
@ -1109,8 +1108,7 @@ SpaceBarSettingsView::MessageReceived(BMessage *message)
}
Window()->PostMessage(kSettingsContentsModified);
BMessage notificationMessage;
tracker->SendNotices(kSpaceBarColorChanged, &notificationMessage);
tracker->PostMessage(kSpaceBarColorChanged);
break;
}

View File

@ -483,6 +483,12 @@ TTracker::MessageReceived(BMessage *message)
break;
}
case kShowVolumeSpaceBar:
case kSpaceBarColorChanged: {
gPeriodicUpdatePoses.DoPeriodicUpdate(true);
break;
}
default:
_inherited::MessageReceived(message);
break;
@ -497,20 +503,7 @@ TTracker::Pulse()
return;
// update the volume icon's free space bars
BVolumeRoster roster;
BVolume volume;
while (roster.GetNextVolume(&volume) == B_OK) {
BDirectory dir;
volume.GetRootDirectory(&dir);
node_ref nodeRef;
dir.GetNodeRef(&nodeRef);
BMessage notificationMessage;
notificationMessage.AddInt32("device", *(int32 *)&nodeRef.device);
SendNotices(kUpdateVolumeSpaceBar, &notificationMessage);
}
gPeriodicUpdatePoses.DoPeriodicUpdate(false);
}

View File

@ -35,6 +35,7 @@ All rights reserved.
#include "Attributes.h"
#include "MimeTypes.h"
#include "Model.h"
#include "PoseView.h"
#include "Utilities.h"
#include "ContainerWindow.h"
@ -158,7 +159,81 @@ DisallowMetaKeys(BTextView *textView)
textView->DisallowChar(B_PAGE_DOWN);
textView->DisallowChar(B_FUNCTION_KEY);
}
PeriodicUpdatePoses::PeriodicUpdatePoses()
: fPoseList(20, true)
{
fLock = new Benaphore("PeriodicUpdatePoses");
}
PeriodicUpdatePoses::~PeriodicUpdatePoses()
{
fLock->Lock();
fPoseList.MakeEmpty();
delete fLock;
}
void
PeriodicUpdatePoses::AddPose(BPose *pose, BPoseView *poseView,
PeriodicUpdateCallback callback, void *cookie)
{
periodic_pose *periodic = new periodic_pose;
periodic->pose = pose;
periodic->pose_view = poseView;
periodic->callback = callback;
periodic->cookie = cookie;
fPoseList.AddItem(periodic);
}
bool
PeriodicUpdatePoses::RemovePose(BPose *pose, void **cookie)
{
int32 count = fPoseList.CountItems();
for (int32 index = 0; index < count; index++) {
if (fPoseList.ItemAt(index)->pose == pose) {
if (!fLock->Lock())
return false;
periodic_pose *periodic = fPoseList.RemoveItemAt(index);
if (cookie)
*cookie = periodic->cookie;
delete periodic;
fLock->Unlock();
return true;
}
}
return false;
}
void
PeriodicUpdatePoses::DoPeriodicUpdate(bool forceRedraw)
{
if (!fLock->Lock())
return;
int32 count = fPoseList.CountItems();
for (int32 index = 0; index < count; index++) {
periodic_pose *periodic = fPoseList.ItemAt(index);
if (periodic->callback(periodic->pose, periodic->cookie)
|| forceRedraw) {
periodic->pose_view->LockLooper();
periodic->pose_view->UpdateIcon(periodic->pose);
periodic->pose_view->UnlockLooper();
}
}
fLock->Unlock();
}
static PeriodicUpdatePoses gPeriodicUpdatePoses;
} // namespace BPrivate

View File

@ -46,6 +46,7 @@ All rights reserved.
#include <MenuItem.h>
#include <MessageFilter.h>
#include <Mime.h>
#include <ObjectList.h>
#include <Point.h>
#include <Path.h>
#include <String.h>
@ -62,6 +63,8 @@ class BView;
namespace BPrivate {
class Benaphore;
class BPose;
class BPoseView;
// global variables
extern const rgb_color kBlack;
@ -83,6 +86,38 @@ const color_space kDefaultIconDepth = B_CMAP8;
// misc typedefs, constants and structs
// Periodically updated poses (ones with a volume space bar) register
// themselfs in this global list. This way they can be iterated over instead
// of sending around update messages.
class PeriodicUpdatePoses {
public:
PeriodicUpdatePoses();
~PeriodicUpdatePoses();
typedef bool (*PeriodicUpdateCallback)(BPose *pose, void *cookie);
void AddPose(BPose *pose, BPoseView *poseView,
PeriodicUpdateCallback callback, void *cookie);
bool RemovePose(BPose *pose, void **cookie);
void DoPeriodicUpdate(bool forceRedraw);
private:
struct periodic_pose {
BPose *pose;
BPoseView *pose_view;
PeriodicUpdateCallback callback;
void *cookie;
};
Benaphore *fLock;
BObjectList<periodic_pose> fPoseList;
};
extern PeriodicUpdatePoses gPeriodicUpdatePoses;
// PoseInfo is the structure that gets saved as attributes for every node on
// disk, defining the node's position and visibility
class PoseInfo {