[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-core-devel
Subject:    Re: Making KMemoryInfo useful (Re: KDE/kdelibs/kdecore)
From:       Lubos Lunak <l.lunak () suse ! cz>
Date:       2010-03-20 19:54:59
Message-ID: 201003202054.59814.l.lunak () suse ! cz
[Download RAW message or body]

On Saturday 20 of March 2010, Albert Astals Cid wrote:
> A Dissabte, 20 de març de 2010, Lubos Lunak va escriure:
> >  Let's continue only on k-c-d.
> >
> > ... Some bashing ...

 I apologize, it wasn't meant to be. And I don't think it was that bad, 
although quite possibly I have already seen enough cases of memory measuring 
going wrong and really wasn't happy to see kdelibs getting a tool to do it 
even easier.

> Ok, so you are the smart dude, how do we solve the problem with
> applications wanting to know how much memory they can use?

 See attachment, that is how I would envision the class. I didn't update the 
docs and didn't bother with implementing requestMemory() for real, but 
otherwise I would hope this is quite clear even without docs:

...
connect( &memoryInfo, SIGNAL( releaseMemoryRequest( size_t )), this, SLOT( 
releaseMemory( size_t )));
...

if( I_want_100M_for_cache && memoryInfo.update( KMemoryInfo::AvailableRam ) && 
memoryInfo.detail( KMemoryInfo::AvailableRam ) >= 100 * 1024 * 1024 )
   ...

if( I_need_300M_to_work )
    {
    if( memoryInfo.update( KMemoryInfo::AvailableMemory ) && 
memoryInfo.detail( KMemoryInfo::AvailableMemory ) >= 300 * 1024 * 1024 )
       do_work();
    else if( memoryInfo.requestMemory( 300 * 1024 * 1024 ))
        do_work();
    ...

void Foo::releaseMemory( size_t amount )
    {
    size_t freed = 0;
    while( !cachedObjects.isEmpty() && freed < amount )
        {
        freed += cachedObjects.first()->size();
        delete cachedObjects.takeFirst();
        }
    }

 Any problems with that?

 Special bonus: 4.5 release blahblah can somewhere include something along the 
lines of "KDE platform now includes support for applications making use of 
additional memory when available and keeping their memory usage low when 
running short on memory or on low-memory systems". And if people get it wrong 
as is the usual case for anything memory-related, with a little luck they may 
at least get it wrong the favourable way for a change ("well, yes, my KDE 
uses 800M RAM, but that's only because my computer has 2G RAM, see?").

-- 
 Lubos Lunak
 openSUSE Boosters team, KDE developer
 l.lunak@suse.cz , l.lunak@kde.org

["util.patch" (text/x-diff)]

--- util/kmemoryinfo.h.sav	2010-03-20 20:02:33.014669220 +0100
+++ util/kmemoryinfo.h	2010-03-20 20:35:42.300591488 +0100
@@ -22,6 +22,8 @@
 
 #include <kdecore_export.h>
 
+#include <QtCore/QObject>
+
 class QDateTime;
 class KMemoryInfoData;
 
@@ -47,21 +49,23 @@ class KMemoryInfoData;
  *
  * @since 4.5
  */
-class KDECORE_EXPORT KMemoryInfo
+class KDECORE_EXPORT KMemoryInfo : public QObject
 {
+Q_OBJECT
 public:
     /**
      * A detail of memory.
      */
     enum MemoryDetail
     {
-        TotalRam = 1,
-        FreeRam = 2,
-        SharedRam = 4,
-        BufferRam = 8,
-        CachedRam = 16,
-        TotalSwap = 32,
-        FreeSwap = 64
+        TotalRam = 1 << 0,
+        AvailableRam = 1 << 1,
+        TotalSwap = 1 << 2,
+        AvailableSwap = 1 << 3,
+        TotalMemory = TotalRam | TotalSwap,
+        AvailableMemory = AvailableRam | AvailableSwap,
+        SystemBuffers = 1 << 4,
+        SystemCaches = 1 << 5
     };
     Q_DECLARE_FLAGS(MemoryDetails, MemoryDetail)
 
@@ -69,28 +73,19 @@ public:
      * Constructs a memory information object which has no updated information.
      * @see update()
      */
-    explicit KMemoryInfo();
-    /**
-     * Copy constructor.
-     */
-    KMemoryInfo(const KMemoryInfo &info);
+    KMemoryInfo();
     /**
      * Destructor.
      */
     ~KMemoryInfo();
 
     /**
-     * Assignment operator.
-     */
-    KMemoryInfo& operator=(const KMemoryInfo &info);
-
-    /**
      * Returns the specified memory @p detail, as it was read by the last
      * update().
      * @returns the value of the specified detail if available, or -1 if that
      *          detail was not requested in the last update()
      */
-    qint64 detail(MemoryDetail detail) const;
+    size_t detail(MemoryDetail detail) const;
     /**
      * Returns the timestamp of the last update, or a null one if the current
      * memory information was never updated.
@@ -107,6 +102,11 @@ public:
      */
     bool update(MemoryDetails details);
 
+    bool requestMemory( size_t amount );
+
+Q_SIGNALS:
+    void releaseMemoryRequest( size_t amount );
+
 private:
     KMemoryInfoData *d;
 };
--- util/kmemoryinfo_win.cpp.sav	2010-03-20 19:52:41.000000000 +0100
+++ util/kmemoryinfo_win.cpp	2010-03-20 20:15:38.375669732 +0100
@@ -32,18 +32,15 @@ static bool fillMemoryInfo(KMemoryInfoDa
     if (data->details & KMemoryInfo::TotalRam) {
         data->totalRam = stat.ullTotalPhys;
     }
-    if (data->details & KMemoryInfo::FreeRam) {
+    if (data->details & KMemoryInfo::AvailableRam) {
         data->freeRam = stat.ullAvailPhys;
     }
-    // the following three are not available
-    if (data->details & KMemoryInfo::SharedRam) {
-        data->sharedRam = 0;
+    // the following two are not available
+    if (data->details & KMemoryInfo::SystemBuffers) {
+        data->systemBuffers = 0;
     }
-    if (data->details & KMemoryInfo::BufferRam) {
-        data->bufferRam = 0;
-    }
-    if (data->details & KMemoryInfo::CachedRam) {
-        data->cachedRam = 0;
+    if (data->details & KMemoryInfo::SystemCaches) {
+        data->systemCaches = 0;
     }
     // instead of the size of the swap partition, use the page file instead
     if (data->details & KMemoryInfo::TotalSwap) {
--- util/kmemoryinfo.cpp.sav	2010-03-20 19:52:40.000000000 +0100
+++ util/kmemoryinfo.cpp	2010-03-20 20:37:30.551669277 +0100
@@ -33,22 +33,20 @@ struct KMemoryInfoData
 
     KMemoryInfo::MemoryDetails details;
     QDateTime lastUpdate;
-    qint64 totalRam;
-    qint64 freeRam;
-    qint64 sharedRam;
-    qint64 bufferRam;
-    qint64 cachedRam;
-    qint64 totalSwap;
-    qint64 freeSwap;
+    size_t totalRam;
+    size_t freeRam;
+    size_t systemBuffers;
+    size_t systemCaches;
+    size_t totalSwap;
+    size_t freeSwap;
 };
 
 void KMemoryInfoData::clear()
 {
     totalRam = -1;
     freeRam = -1;
-    sharedRam = -1;
-    bufferRam = -1;
-    cachedRam = -1;
+    systemBuffers = -1;
+    systemCaches = -1;
     totalSwap = -1;
     freeSwap = -1;
 }
@@ -61,41 +59,31 @@ KMemoryInfo::KMemoryInfo()
 {
 }
 
-KMemoryInfo::KMemoryInfo(const KMemoryInfo &info)
-    : d(new KMemoryInfoData(*info.d))
-{
-}
-
 KMemoryInfo::~KMemoryInfo()
 {
     delete d;
 }
 
-KMemoryInfo& KMemoryInfo::operator=(const KMemoryInfo &info)
-{
-    if (this != &info) {
-        *d = *info.d;
-    }
-    return *this;
-}
-
-qint64 KMemoryInfo::detail(KMemoryInfo::MemoryDetail detail) const
+size_t KMemoryInfo::detail(KMemoryInfo::MemoryDetail detail) const
 {
     switch (detail) {
     case KMemoryInfo::TotalRam:
         return d->totalRam;
-    case KMemoryInfo::FreeRam:
-        return d->freeRam;
-    case KMemoryInfo::SharedRam:
-        return d->sharedRam;
-    case KMemoryInfo::BufferRam:
-        return d->bufferRam;
-    case KMemoryInfo::CachedRam:
-        return d->cachedRam;
+    case KMemoryInfo::AvailableRam:
+    // the system may free buffers and caches if necessary, so it's available in practice
+        return d->freeRam + d->systemBuffers + d->systemCaches;
+    case KMemoryInfo::SystemBuffers:
+        return d->systemBuffers;
+    case KMemoryInfo::SystemCaches:
+        return d->systemCaches;
     case KMemoryInfo::TotalSwap:
         return d->totalSwap;
-    case KMemoryInfo::FreeSwap:
+    case KMemoryInfo::AvailableSwap:
         return d->freeSwap;
+    case KMemoryInfo::TotalMemory:
+        return this->detail( TotalRam ) + this->detail( TotalSwap );
+    case KMemoryInfo::AvailableMemory:
+        return this->detail( AvailableRam ) + this->detail( AvailableSwap );
     }
 
     return -1;
@@ -120,6 +108,16 @@ bool KMemoryInfo::update(KMemoryInfo::Me
     return res;
 }
 
+bool KMemoryInfo::requestMemory( size_t amount )
+{
+// TODO this should signal to other apps and emit releaseMemoryRequest()
+// in them until available memory is at least the given amount.
+// For now, just check.
+    if( !update( d->details | AvailableRam | AvailableSwap ))
+        return false;
+    return detail( AvailableRam ) + detail( AvailableSwap ) >= amount;
+}
+
 #if defined(Q_OS_LINUX)
 #  include "kmemoryinfo_linux.cpp"
 #elif defined(Q_OS_WIN32)
@@ -138,3 +136,4 @@ static bool fillMemoryInfo(KMemoryInfoDa
 
 #endif
 
+#include "kmemoryinfo.moc"
--- util/kmemoryinfo_linux.cpp.sav	2010-03-20 19:52:40.000000000 +0100
+++ util/kmemoryinfo_linux.cpp	2010-03-20 20:15:30.665669799 +0100
@@ -33,32 +33,29 @@ static bool fillMemoryInfo(KMemoryInfoDa
     if (data->details & KMemoryInfo::TotalRam) {
         data->totalRam = unit * info.totalram;
     }
-    if (data->details & KMemoryInfo::FreeRam) {
+    if (data->details & KMemoryInfo::AvailableRam) {
         data->freeRam = unit * info.freeram;
     }
-    if (data->details & KMemoryInfo::SharedRam) {
-        data->sharedRam = unit * info.sharedram;
-    }
-    if (data->details & KMemoryInfo::BufferRam) {
-        data->bufferRam = unit * info.bufferram;
+    if (data->details & KMemoryInfo::SystemBuffers) {
+        data->systemBuffers = unit * info.bufferram;
     }
     if (data->details & KMemoryInfo::TotalSwap) {
         data->totalSwap = unit * info.totalswap;
     }
-    if (data->details & KMemoryInfo::FreeSwap) {
+    if (data->details & KMemoryInfo::AvailableSwap) {
         data->freeSwap = unit * info.freeswap;
     }
 
     // open meminfo only for reading "cached"
-    if (data->details & KMemoryInfo::CachedRam) {
-        data->cachedRam = 0;
+    if (data->details & KMemoryInfo::SystemCaches) {
+        data->systemCaches = 0;
         FILE *meminfo = fopen("/proc/meminfo", "r");
         if (meminfo) {
             char buffer[100];
             char *end = 0;
             while(fgets(buffer, sizeof(buffer) - 1, meminfo)) {
                 if (memcmp(buffer, "Cached:", 7) == 0) {
-                    data->cachedRam = strtol(buffer + 7, &end, 10);
+                    data->systemCaches = strtol(buffer + 7, &end, 10);
                     break;
                 }
             }


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic