Inside StoreEntry::swapOut() the StoreEntry::trimMemory() method called
to release unused MemObjects memory. The trimMemory method must called
for all store entries, but current code blocks that call for
not-swappable objects, at least.

This patch trying to fix this bug implementing the following simple logic:
   {
     bool weAreOrMayBeSwappingOut =
           swappingOut() || mayStartSwapout();

     trimMemory(weAreOrMayBeSwappingOut);

     if (!weAreOrMayBeSwappingOut)
         return; // nothing else to do
   }

This is a Measurement Factory project
trimMemory for unswappable objects

Inside StoreEntry::swapOut() the StoreEntry::trimMemory() method called to 
release unused MemObjects memory. The trimMemory method must called for all
store entries, but current code blocks that call for not-swappable objects, 
at least. 

This patch trying to fix this bug implementing the following simple logic:
   {
     bool weAreOrMayBeSwappingOut =
           swappingOut() || mayStartSwapout();

     trimMemory(weAreOrMayBeSwappingOut);

     if (!weAreOrMayBeSwappingOut)
         return; // nothing else to do 
   }
=== modified file 'src/MemObject.cc'
--- src/MemObject.cc	2011-09-06 22:32:30 +0000
+++ src/MemObject.cc	2011-11-23 12:42:07 +0000
@@ -475,20 +475,26 @@
             continue;
 
 #endif
 
         if (sc->getType() != STORE_MEM_CLIENT)
             /* reading off disk */
             continue;
 
         j = sc->delayId.bytesWanted(0, sc->copyInto.length);
 
         if (j > jmax) {
             jmax = j;
             result = sc->delayId;
         }
     }
 
     return result;
 }
 
 #endif
+
+int64_t
+MemObject::availableForSwapOut() const
+{
+    return endOffset() - swapout.queue_offset;
+}

=== modified file 'src/MemObject.h'
--- src/MemObject.h	2011-04-28 22:45:55 +0000
+++ src/MemObject.h	2012-01-07 09:57:12 +0000
@@ -64,40 +64,41 @@
     void write(StoreIOBuffer, STMCB *, void *);
     void unlinkRequest();
     HttpReply const *getReply() const;
     void replaceHttpReply(HttpReply *newrep);
     void stat (MemBuf * mb) const;
     int64_t endOffset () const;
     void markEndOfReplyHeaders(); ///< sets _reply->hdr_sz to endOffset()
     /// negative if unknown; otherwise, expected object_sz, expected endOffset
     /// maximum, and stored reply headers+body size (all three are the same)
     int64_t expectedReplySize() const;
     int64_t size() const;
     void reset();
     int64_t lowestMemReaderOffset() const;
     bool readAheadPolicyCanRead() const;
     void addClient(store_client *);
     /* XXX belongs in MemObject::swapout, once swaphdrsz is managed
      * better
      */
     int64_t objectBytesOnDisk() const;
     int64_t policyLowestOffsetToKeep(bool swap) const;
+    int64_t availableForSwapOut() const; ///< buffered bytes we have not swapped out yet
     void trimSwappable();
     void trimUnSwappable();
     bool isContiguous() const;
     int mostBytesWanted(int max) const;
     void setNoDelay(bool const newValue);
 #if USE_DELAY_POOLS
     DelayId mostBytesAllowed() const;
 #endif
 
 
 #if URL_CHECKSUM_DEBUG
 
     void checkUrlChecksum() const;
 #endif
 
     HttpRequestMethod method;
     char *url;
     mem_hdr data_hdr;
     int64_t inmem_lo;
     dlink_list clients;

=== modified file 'src/Store.h'
--- src/Store.h	2011-11-18 16:53:45 +0000
+++ src/Store.h	2011-11-23 15:23:13 +0000
@@ -75,57 +75,59 @@
 
 public:
     static DeferredRead::DeferrableRead DeferReader;
     bool checkDeferRead(int fd) const;
 
     virtual const char *getMD5Text() const;
     StoreEntry();
     StoreEntry(const char *url, const char *log_url);
     virtual ~StoreEntry();
 
     virtual HttpReply const *getReply() const;
     virtual void write (StoreIOBuffer);
     virtual _SQUID_INLINE_ bool isEmpty() const;
     virtual bool isAccepting() const;
     virtual size_t bytesWanted(Range<size_t> const) const;
     virtual void complete();
     virtual store_client_t storeClientType() const;
     virtual char const *getSerialisedMetaData();
     void replaceHttpReply(HttpReply *, bool andStartWriting = true);
     void startWriting(); ///< pack and write reply headers and, maybe, body
-    virtual bool swapoutPossible();
-    virtual void trimMemory();
+    /// whether we may start writing to disk (now or in the future)
+    virtual bool mayStartSwapOut();
+    virtual void trimMemory(const bool preserveSwappable);
     void abort();
     void unlink();
     void makePublic();
     void makePrivate();
     void setPublicKey();
     void setPrivateKey();
     void expireNow();
     void releaseRequest();
     void negativeCache();
     void cacheNegatively();		/** \todo argh, why both? */
     void invokeHandlers();
     void purgeMem();
     void cacheInMemory(); ///< start or continue storing in memory cache
     void swapOut();
-    bool swapOutAble() const;
+    /// whether we are in the process of writing this entry to disk
+    bool swappingOut() const { return swap_status == SWAPOUT_WRITING; }
     void swapOutFileClose(int how);
     const char *url() const;
     int checkCachable();
     int checkNegativeHit() const;
     int locked() const;
     int validToSend() const;
     bool memoryCachable() const; ///< may be cached in memory
     void createMemObject(const char *, const char *);
     void hideMemObject(); ///< no mem_obj for callers until createMemObject
     void dump(int debug_lvl) const;
     void hashDelete();
     void hashInsert(const cache_key *);
     void registerAbort(STABH * cb, void *);
     void reset();
     void setMemStatus(mem_status_t);
     void timestampsSet();
     void unregisterAbort();
     void destroyMemObject();
     int checkTooSmall();
 
@@ -230,41 +232,41 @@
     static NullStoreEntry *getInstance();
     bool isNull() {
         return true;
     }
 
     const char *getMD5Text() const;
     _SQUID_INLINE_ HttpReply const *getReply() const;
     void write (StoreIOBuffer) {}
 
     bool isEmpty () const {return true;}
 
     virtual size_t bytesWanted(Range<size_t> const aRange) const { assert (aRange.size()); return aRange.end - 1;}
 
     void operator delete(void *address);
     void complete() {}
 
 private:
     store_client_t storeClientType() const {return STORE_MEM_CLIENT;}
 
     char const *getSerialisedMetaData();
-    bool swapoutPossible() {return false;}
+    bool mayStartSwapout() {return false;}
 
     void trimMemory() {}
 
 
     static NullStoreEntry _instance;
 };
 
 /// \ingroup StoreAPI
 typedef void (*STOREGETCLIENT) (StoreEntry *, void *cbdata);
 
 
 /**
  \ingroup StoreAPI
  * Abstract base class that will replace the whole store and swapdir interface.
  */
 class Store : public RefCountable
 {
 
 public:
     /** The root store */

=== modified file 'src/client_side.cc'
--- src/client_side.cc	2011-12-30 12:26:51 +0000
+++ src/client_side.cc	2012-01-09 11:11:15 +0000
@@ -2685,41 +2685,41 @@
             context->mayUseConnection(true);
             assert(conn->flags.readMore);
         }
     }
 
     http->calloutContext = new ClientRequestContext(http);
 
     http->doCallouts();
 
 finish:
     if (!notedUseOfBuffer)
         connNoteUseOfBuffer(conn, http->req_sz);
 
     /*
      * DPW 2007-05-18
      * Moved the TCP_RESET feature from clientReplyContext::sendMoreData
      * to here because calling comm_reset_close() causes http to
      * be freed and the above connNoteUseOfBuffer() would hit an
      * assertion, not to mention that we were accessing freed memory.
      */
-    if (http->request->flags.resetTCP() && Comm::IsConnOpen(conn->clientConnection)) {
+    if (request && request->flags.resetTCP() && Comm::IsConnOpen(conn->clientConnection)) {
         debugs(33, 3, HERE << "Sending TCP RST on " << conn->clientConnection);
         conn->flags.readMore = false;
         comm_reset_close(conn->clientConnection);
     }
 }
 
 static void
 connStripBufferWhitespace (ConnStateData * conn)
 {
     while (conn->in.notYetUsed > 0 && xisspace(conn->in.buf[0])) {
         memmove(conn->in.buf, conn->in.buf + 1, conn->in.notYetUsed - 1);
         --conn->in.notYetUsed;
     }
 }
 
 static int
 connOkToAddRequest(ConnStateData * conn)
 {
     int result = conn->getConcurrentRequestCount() < (Config.onoff.pipeline_prefetch ? 2 : 1);
 

=== modified file 'src/store.cc'
--- src/store.cc	2011-12-10 16:29:31 +0000
+++ src/store.cc	2012-01-07 09:34:28 +0000
@@ -1874,139 +1874,52 @@
     mem_obj->markEndOfReplyHeaders();
 
     rep->body.packInto(&p);
 
     packerClean(&p);
 }
 
 
 char const *
 StoreEntry::getSerialisedMetaData()
 {
     StoreMeta *tlv_list = storeSwapMetaBuild(this);
     int swap_hdr_sz;
     char *result = storeSwapMetaPack(tlv_list, &swap_hdr_sz);
     storeSwapTLVFree(tlv_list);
     assert (swap_hdr_sz >= 0);
     mem_obj->swap_hdr_sz = (size_t) swap_hdr_sz;
     return result;
 }
 
-bool
-StoreEntry::swapoutPossible()
-{
-    if (!Config.cacheSwap.n_configured)
-        return false;
-
-    /* should we swap something out to disk? */
-    debugs(20, 7, "storeSwapOut: " << url());
-    debugs(20, 7, "storeSwapOut: store_status = " << storeStatusStr[store_status]);
-
-    assert(mem_obj);
-    MemObject::SwapOut::Decision &decision = mem_obj->swapout.decision;
-
-    // if we decided that swapout is not possible, do not repeat same checks
-    if (decision == MemObject::SwapOut::swImpossible) {
-        debugs(20, 3, "storeSwapOut: already rejected");
-        return false;
-    }
-
-    // this flag may change so we must check it even if we already said "yes"
-    if (EBIT_TEST(flags, ENTRY_ABORTED)) {
-        assert(EBIT_TEST(flags, RELEASE_REQUEST));
-        // StoreEntry::abort() already closed the swap out file, if any
-        decision = MemObject::SwapOut::swImpossible;
-        return false;
-    }
-
-    // if we decided that swapout is possible, do not repeat same checks
-    if (decision == MemObject::SwapOut::swPossible) {
-        debugs(20, 3, "storeSwapOut: already allowed");
-        return true;
-    }
-
-    // if we are swapping out already, do not repeat same checks
-    if (swap_status != SWAPOUT_NONE) {
-        debugs(20, 3, "storeSwapOut: already started");
-        decision = MemObject::SwapOut::swPossible;
-        return true;
-    }
-
-    if (!checkCachable()) {
-        debugs(20, 3, "storeSwapOut: not cachable");
-        decision = MemObject::SwapOut::swImpossible;
-        return false;
-    }
-
-    if (EBIT_TEST(flags, ENTRY_SPECIAL)) {
-        debugs(20, 3, "storeSwapOut: " << url() << " SPECIAL");
-        decision = MemObject::SwapOut::swImpossible;
-        return false;
-    }
-
-    // check cache_dir max-size limit if all cache_dirs have it
-    if (store_maxobjsize >= 0) {
-        // TODO: add estimated store metadata size to be conservative
-
-        // use guaranteed maximum if it is known
-        const int64_t expectedEnd = mem_obj->expectedReplySize();
-        debugs(20, 7, "storeSwapOut: expectedEnd = " << expectedEnd);
-        if (expectedEnd > store_maxobjsize) {
-            debugs(20, 3, "storeSwapOut: will not fit: " << expectedEnd <<
-                   " > " << store_maxobjsize);
-            decision = MemObject::SwapOut::swImpossible;
-            return false; // known to outgrow the limit eventually
-        }
-
-        // use current minimum (always known)
-        const int64_t currentEnd = mem_obj->endOffset();
-        if (currentEnd > store_maxobjsize) {
-            debugs(20, 3, "storeSwapOut: does not fit: " << currentEnd <<
-                   " > " << store_maxobjsize);
-            decision = MemObject::SwapOut::swImpossible;
-            return false; // already does not fit and may only get bigger
-        }
-
-        // prevent default swPossible answer for yet unknown length
-        if (expectedEnd < 0) {
-            debugs(20, 3, "storeSwapOut: wait for more info: " <<
-                   store_maxobjsize);
-            return false; // may fit later, but will be rejected now
-        }
-    }
-
-    decision = MemObject::SwapOut::swPossible;
-    return true;
-}
-
 void
-StoreEntry::trimMemory()
+StoreEntry::trimMemory(const bool preserveSwappable)
 {
     /*
      * DPW 2007-05-09
      * Bug #1943.  We must not let go any data for IN_MEMORY
      * objects.  We have to wait until the mem_status changes.
      */
     if (mem_status == IN_MEMORY)
         return;
 
-    if (!swapOutAble()) {
+    if (!preserveSwappable) {
         if (mem_obj->policyLowestOffsetToKeep(0) == 0) {
             /* Nothing to do */
             return;
         }
         /*
          * Its not swap-able, and we're about to delete a chunk,
          * so we must make it PRIVATE.  This is tricky/ugly because
          * for the most part, we treat swapable == cachable here.
          */
         releaseRequest();
         mem_obj->trimUnSwappable ();
     } else {
         mem_obj->trimSwappable ();
     }
 }
 
 bool
 StoreEntry::modifiedSince(HttpRequest * request) const
 {
     int object_length;

=== modified file 'src/store_client.cc'
--- src/store_client.cc	2011-12-10 16:29:31 +0000
+++ src/store_client.cc	2012-01-07 09:34:28 +0000
@@ -178,41 +178,41 @@
     if (!sc->_callback.pending())
         return;
 
     storeClientCopy2(sc->entry, sc);
 }
 
 store_client::store_client(StoreEntry *e) : entry (e)
 #if USE_DELAY_POOLS
         , delayId()
 #endif
         , type (e->storeClientType())
         ,  object_ok(true)
 {
     cmp_offset = 0;
     flags.disk_io_pending = 0;
     entry->refcount++;
 
     if (getType() == STORE_DISK_CLIENT)
         /* assert we'll be able to get the data we want */
         /* maybe we should open swapin_sio here */
-        assert(entry->swap_filen > -1 || entry->swapOutAble());
+        assert(entry->swap_filen > -1 || entry->swappingOut());
 
 #if STORE_CLIENT_LIST_DEBUG
 
     owner = cbdataReference(data);
 
 #endif
 }
 
 store_client::~store_client()
 {}
 
 /* copy bytes requested by the client */
 void
 storeClientCopy(store_client * sc,
                 StoreEntry * e,
                 StoreIOBuffer copyInto,
                 STCB * callback,
                 void *data)
 {
     assert (sc != NULL);

=== modified file 'src/store_swapout.cc'
--- src/store_swapout.cc	2011-12-10 16:29:31 +0000
+++ src/store_swapout.cc	2012-01-10 09:48:22 +0000
@@ -171,101 +171,84 @@
 
         if (anEntry->store_status == STORE_PENDING)
             if (swapout_size < SM_PAGE_SIZE)
                 break;
 
         if (swapout_size <= 0)
             return;
     } while (true);
 }
 
 
 /* This routine is called every time data is sent to the client side.
  * It's overhead is therefor, significant.
  */
 void
 StoreEntry::swapOut()
 {
     if (!mem_obj)
         return;
 
-    if (!swapoutPossible())
+    // this flag may change so we must check even if we are swappingOut
+    if (EBIT_TEST(flags, ENTRY_ABORTED)) {
+        assert(EBIT_TEST(flags, RELEASE_REQUEST));
+        // StoreEntry::abort() already closed the swap out file, if any
+        // no trimming: data producer must stop production if ENTRY_ABORTED
         return;
+    }
+
+    const bool weAreOrMayBeSwappingOut = swappingOut() || mayStartSwapOut();
+ 
+    trimMemory(weAreOrMayBeSwappingOut);
+
+    if (!weAreOrMayBeSwappingOut)
+        return; // nothing else to do
 
     // Aborted entries have STORE_OK, but swapoutPossible rejects them. Thus,
     // store_status == STORE_OK below means we got everything we wanted.
 
     debugs(20, 7, HERE << "storeSwapOut: mem->inmem_lo = " << mem_obj->inmem_lo);
     debugs(20, 7, HERE << "storeSwapOut: mem->endOffset() = " << mem_obj->endOffset());
     debugs(20, 7, HERE << "storeSwapOut: swapout.queue_offset = " << mem_obj->swapout.queue_offset);
 
     if (mem_obj->swapout.sio != NULL)
         debugs(20, 7, "storeSwapOut: storeOffset() = " << mem_obj->swapout.sio->offset()  );
 
-    // buffered bytes we have not swapped out yet
-    int64_t swapout_maxsize = mem_obj->endOffset() - mem_obj->swapout.queue_offset;
-
-    assert(swapout_maxsize >= 0);
-
     int64_t const lowest_offset = mem_obj->lowestMemReaderOffset();
 
     debugs(20, 7, HERE << "storeSwapOut: lowest_offset = " << lowest_offset);
 
-    // Check to see whether we're going to defer the swapout based upon size
-    if (store_status != STORE_OK) {
-        const int64_t expectedSize = mem_obj->expectedReplySize();
-        const int64_t maxKnownSize = expectedSize < 0 ?
-                                     swapout_maxsize : expectedSize;
-        debugs(20, 7, HERE << "storeSwapOut: maxKnownSize= " << maxKnownSize);
-
-        if (maxKnownSize < store_maxobjsize) {
-            /*
-             * NOTE: the store_maxobjsize here is the max of optional
-             * max-size values from 'cache_dir' lines.  It is not the
-             * same as 'maximum_object_size'.  By default, store_maxobjsize
-             * will be set to -1.  However, I am worried that this
-             * deferance may consume a lot of memory in some cases.
-             * Should we add an option to limit this memory consumption?
-             */
-            debugs(20, 5, "storeSwapOut: Deferring swapout start for " <<
-                   (store_maxobjsize - maxKnownSize) << " bytes");
-            return;
-        }
-    }
-
-// TODO: it is better to trim as soon as we swap something out, not before
-    trimMemory();
 #if SIZEOF_OFF_T <= 4
 
     if (mem_obj->endOffset() > 0x7FFF0000) {
         debugs(20, 0, "WARNING: preventing off_t overflow for " << url());
         abort();
         return;
     }
 
 #endif
     if (swap_status == SWAPOUT_WRITING)
         assert(mem_obj->inmem_lo <=  mem_obj->objectBytesOnDisk() );
 
-    if (!swapOutAble())
-        return;
-
+    // buffered bytes we have not swapped out yet
+    const int64_t swapout_maxsize = mem_obj->availableForSwapOut();
+    assert(swapout_maxsize >= 0);
     debugs(20, 7, "storeSwapOut: swapout_size = " << swapout_maxsize);
 
     if (swapout_maxsize == 0) { // swapped everything we got
         if (store_status == STORE_OK) { // got everything we wanted
             assert(mem_obj->object_sz >= 0);
             swapOutFileClose(StoreIOState::wroteAll);
         }
         // else need more data to swap out
         return;
     }
 
     if (store_status == STORE_PENDING) {
         /* wait for a full block to write */
 
         if (swapout_maxsize < SM_PAGE_SIZE)
             return;
 
         /*
          * Wait until we are below the disk FD limit, only if the
          * next server-side read won't be deferred.
@@ -357,59 +340,154 @@
         e->swap_status = SWAPOUT_DONE;
         e->store()->swappedOut(*e);
 
         // XXX: For some Stores, it is pointless to re-check cachability here
         // and it leads to double counts in store_check_cachable_hist. We need
         // another way to signal a completed but failed swapout. Or, better,
         // each Store should handle its own logging and LOG state setting.
         if (e->checkCachable()) {
             storeLog(STORE_LOG_SWAPOUT, e);
             storeDirSwapLog(e, SWAP_LOG_ADD);
         }
 
         ++statCounter.swap.outs;
     }
 
     debugs(20, 3, "storeSwapOutFileClosed: " << __FILE__ << ":" << __LINE__);
     mem->swapout.sio = NULL;
     e->unlock();
 }
 
-/*
- * Is this entry a candidate for writing to disk?
- */
 bool
-StoreEntry::swapOutAble() const
+StoreEntry::mayStartSwapOut()
 {
     dlink_node *node;
 
-    if (mem_obj->swapout.sio != NULL)
+    // must be checked in the caller
+    assert(!EBIT_TEST(flags, ENTRY_ABORTED));
+
+    if (!Config.cacheSwap.n_configured)
+        return false;
+
+    assert(mem_obj);
+    MemObject::SwapOut::Decision &decision = mem_obj->swapout.decision;
+
+    // if we decided that swapout is not possible, do not repeat same checks
+    if (decision == MemObject::SwapOut::swImpossible) {
+        debugs(20, 3, HERE << " already rejected");
+        return false;
+    }
+
+    // if we decided that swapout is possible, do not repeat same checks
+    if (decision == MemObject::SwapOut::swPossible) {
+        debugs(20, 3,  HERE << "already allowed");
         return true;
+    }
 
-    if (mem_obj->inmem_lo > 0)
+    // if we are swapping out already, do not repeat same checks
+    if (swap_status != SWAPOUT_NONE) {
+        debugs(20, 3,  HERE << " already started");
+        decision = MemObject::SwapOut::swPossible;
+        return true;
+    }
+
+    if (!checkCachable()) {
+        debugs(20, 3,  HERE << "not cachable");
+        decision = MemObject::SwapOut::swImpossible;
         return false;
+    }
+
+    if (EBIT_TEST(flags, ENTRY_SPECIAL)) {
+        debugs(20, 3,  HERE  << url() << " SPECIAL");
+        decision = MemObject::SwapOut::swImpossible;
+        return false;
+    }
+
+    // check cache_dir max-size limit if all cache_dirs have it
+    if (store_maxobjsize >= 0) {
+        // TODO: add estimated store metadata size to be conservative
+
+        // use guaranteed maximum if it is known
+        const int64_t expectedEnd = mem_obj->expectedReplySize();
+        debugs(20, 7,  HERE << "expectedEnd = " << expectedEnd);
+        if (expectedEnd > store_maxobjsize) {
+            debugs(20, 3,  HERE << "will not fit: " << expectedEnd <<
+                   " > " << store_maxobjsize);
+            decision = MemObject::SwapOut::swImpossible;
+            return false; // known to outgrow the limit eventually
+        }
+
+        // use current minimum (always known)
+        const int64_t currentEnd = mem_obj->endOffset();
+        if (currentEnd > store_maxobjsize) {
+            debugs(20, 3,  HERE << "does not fit: " << currentEnd <<
+                   " > " << store_maxobjsize);
+            decision = MemObject::SwapOut::swImpossible;
+            return false; // already does not fit and may only get bigger
+        }
+
+        // prevent default swPossible answer for yet unknown length
+        if (expectedEnd < 0) {
+            debugs(20, 3,  HERE << "wait for more info: " <<
+                   store_maxobjsize);
+            return false; // may fit later, but will be rejected now
+        }
+
+        if (store_status != STORE_OK) {
+            const int64_t maxKnownSize = expectedEnd < 0 ?
+                                          mem_obj->availableForSwapOut() : expectedEnd;
+            debugs(20, 7, HERE << "maxKnownSize= " << maxKnownSize);
+            if (maxKnownSize < store_maxobjsize) {
+                /*
+                 * NOTE: the store_maxobjsize here is the max of optional
+                 * max-size values from 'cache_dir' lines.  It is not the
+                 * same as 'maximum_object_size'.  By default, store_maxobjsize
+                 * will be set to -1.  However, I am worried that this
+                 * deferance may consume a lot of memory in some cases.
+                 * Should we add an option to limit this memory consumption?
+                 */
+                debugs(20, 5,  HERE << "Deferring swapout start for " <<
+                       (store_maxobjsize - maxKnownSize) << " bytes");
+                return false;
+            }
+        }
+    }
+
+    if (mem_obj->inmem_lo > 0) {
+        debugs(20, 3, "storeSwapOut: (inmem_lo > 0)  imem_lo:" <<  mem_obj->inmem_lo);
+        decision = MemObject::SwapOut::swImpossible;
+        return false;
+    }
 
     /*
      * If there are DISK clients, we must write to disk
      * even if its not cachable
      * RBC: Surely we should not create disk client on non cacheable objects?
      * therefore this should be an assert?
      * RBC 20030708: We can use disk to avoid mem races, so this shouldn't be
      * an assert.
+     *
+     * XXX: Not clear what "mem races" the above refers to, especially when
+     * dealing with non-cachable objects that cannot have multiple clients.
+     *
+     * XXX: If STORE_DISK_CLIENT needs SwapOut::swPossible, we have to check
+     * for that flag earlier, but forcing swapping may contradict max-size or
+     * other swapability restrictions. Change storeClientType() and/or its
+     * callers to take swap-in availability into account.
      */
     for (node = mem_obj->clients.head; node; node = node->next) {
-        if (((store_client *) node->data)->getType() == STORE_DISK_CLIENT)
+        if (((store_client *) node->data)->getType() == STORE_DISK_CLIENT) {
+            debugs(20, 3, HERE << "DISK client found");
+            decision = MemObject::SwapOut::swPossible;
             return true;
+        }
     }
 
-    /* Don't pollute the disk with icons and other special entries */
-    if (EBIT_TEST(flags, ENTRY_SPECIAL))
-        return false;
-
-    if (!EBIT_TEST(flags, ENTRY_CACHABLE))
-        return false;
-
-    if (!mem_obj->isContiguous())
+    if (!mem_obj->isContiguous()) {
+        debugs(20, 3, "storeSwapOut: not Contiguous");
+        decision = MemObject::SwapOut::swImpossible;
         return false;
+    }
 
+    decision = MemObject::SwapOut::swPossible;
     return true;
 }

=== modified file 'src/tests/stub_MemObject.cc'
--- src/tests/stub_MemObject.cc	2011-10-14 16:21:48 +0000
+++ src/tests/stub_MemObject.cc	2012-01-07 10:29:33 +0000
@@ -189,20 +189,27 @@
 }
 
 void
 MemObject::resetUrls(char const*, char const*)
 {
     fatal ("MemObject.cc required.");
 }
 
 void
 MemObject::markEndOfReplyHeaders()
 {
     fatal ("MemObject.cc required.");
 }
 
 size_t
 MemObject::inUseCount()
 {
     fatal ("MemObject.cc required.");
     return 0;
 }
+
+int64_t
+MemObject::availableForSwapOut() const
+{
+    fatal ("MemObject.cc required.");
+    return 0;
+}

=== modified file 'src/tests/stub_store.cc'
--- src/tests/stub_store.cc	2011-08-10 15:54:51 +0000
+++ src/tests/stub_store.cc	2012-01-07 10:22:54 +0000
@@ -9,56 +9,55 @@
 #include "RemovalPolicy.h"
 RemovalPolicy * createRemovalPolicy(RemovalPolicySettings * settings) STUB_RETVAL(NULL)
 
 
 #include "Store.h"
 StorePointer Store::CurrentRoot = NULL;
 StoreIoStats store_io_stats;
 bool StoreEntry::checkDeferRead(int fd) const STUB_RETVAL(false)
 const char *StoreEntry::getMD5Text() const STUB_RETVAL(NULL)
 StoreEntry::StoreEntry() STUB
 StoreEntry::StoreEntry(const char *url, const char *log_url) STUB
 StoreEntry::~StoreEntry() STUB
 HttpReply const *StoreEntry::getReply() const STUB_RETVAL(NULL)
 void StoreEntry::write(StoreIOBuffer) STUB
 bool StoreEntry::isAccepting() const STUB_RETVAL(false)
 size_t StoreEntry::bytesWanted(Range<size_t> const) const STUB_RETVAL(0)
 void StoreEntry::complete() STUB
 store_client_t StoreEntry::storeClientType() const STUB_RETVAL(STORE_NON_CLIENT)
 char const *StoreEntry::getSerialisedMetaData() STUB_RETVAL(NULL)
 void StoreEntry::replaceHttpReply(HttpReply *, bool andStartWriting) STUB
-bool StoreEntry::swapoutPossible() STUB_RETVAL(false)
-void StoreEntry::trimMemory() STUB
+bool StoreEntry::mayStartSwapOut() STUB_RETVAL(false)
+void StoreEntry::trimMemory(const bool preserveSwappable) STUB
 void StoreEntry::abort() STUB
 void StoreEntry::unlink() STUB
 void StoreEntry::makePublic() STUB
 void StoreEntry::makePrivate() STUB
 void StoreEntry::setPublicKey() STUB
 void StoreEntry::setPrivateKey() STUB
 void StoreEntry::expireNow() STUB
 void StoreEntry::releaseRequest() STUB
 void StoreEntry::negativeCache() STUB
 void StoreEntry::cacheNegatively() STUB
 void StoreEntry::invokeHandlers() STUB
 void StoreEntry::purgeMem() STUB
 void StoreEntry::swapOut() STUB
-bool StoreEntry::swapOutAble() const STUB_RETVAL(false)
 void StoreEntry::swapOutFileClose(int how) STUB
 const char *StoreEntry::url() const STUB_RETVAL(NULL)
 int StoreEntry::checkCachable() STUB_RETVAL(0)
 int StoreEntry::checkNegativeHit() const STUB_RETVAL(0)
 int StoreEntry::locked() const STUB_RETVAL(0)
 int StoreEntry::validToSend() const STUB_RETVAL(0)
 bool StoreEntry::memoryCachable() const STUB_RETVAL(false)
 void StoreEntry::createMemObject(const char *, const char *) STUB
 void StoreEntry::hideMemObject() STUB
 void StoreEntry::dump(int debug_lvl) const STUB
 void StoreEntry::hashDelete() STUB
 void StoreEntry::hashInsert(const cache_key *) STUB
 void StoreEntry::registerAbort(STABH * cb, void *) STUB
 void StoreEntry::reset() STUB
 void StoreEntry::setMemStatus(mem_status_t) STUB
 void StoreEntry::timestampsSet() STUB
 void StoreEntry::unregisterAbort() STUB
 void StoreEntry::destroyMemObject() STUB
 int StoreEntry::checkTooSmall() STUB_RETVAL(0)
 void StoreEntry::delayAwareRead(const Comm::ConnectionPointer&, char *buf, int len, AsyncCall::Pointer callback) STUB

Reply via email to