Server.h \
structs.h \
swap_log_op.h \
- SwapDir.cc \
- SwapDir.h \
+ SwapDir.cc MemStore.cc MemStoreMap.cc \
+ SwapDir.h MemStore.h MemStoreMap.h \
time.cc \
tools.cc \
tunnel.cc \
$(TEST_CALL_SOURCES) \
tools.cc \
tunnel.cc \
- SwapDir.cc \
+ SwapDir.cc MemStore.cc MemStoreMap.cc \
url.cc \
URLScheme.cc \
urn.cc \
$(TEST_CALL_SOURCES) \
tools.cc \
tunnel.cc \
- SwapDir.cc \
+ SwapDir.cc MemStore.cc MemStoreMap.cc \
url.cc \
URLScheme.cc \
urn.cc \
$(TEST_CALL_SOURCES) \
tools.cc \
tunnel.cc \
- SwapDir.cc \
+ SwapDir.cc MemStore.cc MemStoreMap.cc \
url.cc \
URLScheme.cc \
urn.cc \
StoreMetaVary.cc \
StoreSwapLogData.cc \
String.cc \
- SwapDir.cc \
+ SwapDir.cc MemStore.cc MemStoreMap.cc \
$(TEST_CALL_SOURCES) \
time.cc \
tools.cc \
$(TEST_CALL_SOURCES) \
tools.cc \
tunnel.cc \
- SwapDir.cc \
+ SwapDir.cc MemStore.cc MemStoreMap.cc \
url.cc \
URLScheme.cc \
urn.cc \
store_key_md5.cc \
Parsing.cc \
ConfigOption.cc \
- SwapDir.cc \
+ SwapDir.cc MemStore.cc MemStoreMap.cc \
tests/stub_acl.cc tests/stub_cache_cf.cc \
tests/stub_helper.cc cbdata.cc String.cc \
tests/stub_comm.cc \
$(TEST_CALL_SOURCES) \
tools.cc \
tunnel.cc \
- SwapDir.cc \
+ SwapDir.cc MemStore.cc MemStoreMap.cc \
urn.cc \
wccp2.cc \
whois.cc \
return Pool().inUseCount();
}
+void
+MemObject::resetUrls(char const *aUrl, char const *aLog_url)
+{
+ safe_free(url);
+ safe_free(log_url); /* XXX account log_url */
+ log_url = xstrdup(aLog_url);
+ url = xstrdup(aUrl);
+}
+
MemObject::MemObject(char const *aUrl, char const *aLog_url)
{
debugs(20, 3, HERE << "new MemObject " << this);
MemObject(char const *, char const *);
~MemObject();
+ /// replaces construction-time URLs with correct ones; see hidden_mem_obj
+ void resetUrls(char const *aUrl, char const *aLog_url);
+
void write(StoreIOBuffer, STMCB *, void *);
void unlinkRequest();
HttpReply const *getReply() const;
--- /dev/null
+/*
+ * $Id$
+ *
+ * DEBUG: section 20 Memory Cache
+ *
+ */
+
+#include "config.h"
+#include "ipc/mem/Page.h"
+#include "ipc/mem/Pages.h"
+#include "MemObject.h"
+#include "MemStore.h"
+#include "HttpReply.h"
+
+
+// XXX: support storage using more than one page per entry
+
+
+MemStore::MemStore(): map(NULL)
+{
+}
+
+MemStore::~MemStore()
+{
+ delete map;
+}
+
+void
+MemStore::init()
+{
+ if (!map && Config.memMaxSize && (!UsingSmp() || IamWorkerProcess())) {
+ // TODO: warn if we cannot support the configured maximum entry size
+ const int64_t entrySize = Ipc::Mem::PageSize(); // for now
+ const int64_t entryCount = Config.memMaxSize / entrySize;
+ // TODO: warn if we cannot cache at least one item (misconfiguration)
+ if (entryCount > 0)
+ map = new MemStoreMap("cache_mem", entryCount);
+ }
+}
+
+void
+MemStore::stat(StoreEntry &output) const
+{
+ storeAppendPrintf(&output, "Memory Cache");
+ // TODO: implement
+}
+
+void
+MemStore::maintain()
+{
+}
+
+uint64_t
+MemStore::minSize() const
+{
+ return 0; // XXX: irrelevant, but Store parent forces us to implement this
+}
+
+uint64_t
+MemStore::maxSize() const
+{
+ return 0; // XXX: make configurable
+}
+
+void
+MemStore::updateSize(int64_t eSize, int sign)
+{
+ // XXX: irrelevant, but Store parent forces us to implement this
+ fatal("MemStore::updateSize should not be called");
+}
+
+void
+MemStore::reference(StoreEntry &)
+{
+}
+
+void
+MemStore::dereference(StoreEntry &)
+{
+}
+
+int
+MemStore::callback()
+{
+ return 0;
+}
+
+StoreSearch *
+MemStore::search(String const, HttpRequest *)
+{
+ fatal("not implemented");
+ return NULL;
+}
+
+StoreEntry *
+MemStore::get(const cache_key *key)
+{
+ if (!map)
+ return NULL;
+
+ // XXX: replace sfileno with a bigger word (sfileno is only for cache_dirs)
+ sfileno index;
+ const Ipc::StoreMapSlot *const slot = map->openForReading(key, index);
+ if (!slot)
+ return NULL;
+
+ const Ipc::StoreMapSlot::Basics &basics = slot->basics;
+ const MemStoreMap::Extras &extras = map->extras(index);
+
+ // create a brand new store entry and initialize it with stored info
+ StoreEntry *e = new StoreEntry();
+ e->lock_count = 0;
+
+ e->swap_file_sz = basics.swap_file_sz;
+ e->lastref = basics.lastref;
+ e->timestamp = basics.timestamp;
+ e->expires = basics.expires;
+ e->lastmod = basics.lastmod;
+ e->refcount = basics.refcount;
+ e->flags = basics.flags;
+
+ e->store_status = STORE_OK;
+ e->mem_status = IN_MEMORY; // setMemStatus(IN_MEMORY) requires mem_obj
+ //e->swap_status = set in StoreEntry constructor to SWAPOUT_NONE;
+ e->ping_status = PING_NONE;
+
+ EBIT_SET(e->flags, ENTRY_CACHABLE);
+ EBIT_CLR(e->flags, RELEASE_REQUEST);
+ EBIT_CLR(e->flags, KEY_PRIVATE);
+ EBIT_SET(e->flags, ENTRY_VALIDATED);
+
+ const bool copied = copyFromShm(*e, extras);
+
+ // we copied everything we could to local memory; no more need to lock
+ map->closeForReading(index);
+
+ if (copied) {
+ e->hashInsert(key);
+ return e;
+ }
+
+ debugs(20, 3, HERE << "mem-loading failed; freeing " << index);
+ map->free(index); // do not let others into the same trap
+ return NULL;
+}
+
+void
+MemStore::get(String const key, STOREGETCLIENT aCallback, void *aCallbackData)
+{
+ // XXX: not needed but Store parent forces us to implement this
+ fatal("MemStore::get(key,callback,data) should not be called");
+}
+
+bool
+MemStore::copyFromShm(StoreEntry &e, const MemStoreMap::Extras &extras)
+{
+ const Ipc::Mem::PageId &page = extras.page;
+
+ StoreIOBuffer sourceBuf(extras.storedSize, 0,
+ static_cast<char*>(PagePointer(page)));
+
+ // XXX: We do not know the URLs yet, only the key, but we need to parse and
+ // store the response for the Root().get() callers to be happy because they
+ // expect IN_MEMORY entries to already have the response headers and body.
+ // At least one caller calls createMemObject() if there is not one, so
+ // we hide the true object until that happens (to avoid leaking TBD URLs).
+ e.createMemObject("TBD", "TBD");
+
+ // emulate the usual Store code but w/o inapplicable checks and callbacks:
+
+ // from store_client::readBody():
+ HttpReply *rep = (HttpReply *)e.getReply();
+ const ssize_t end = headersEnd(sourceBuf.data, sourceBuf.length);
+ if (!rep->parseCharBuf(sourceBuf.data, end)) {
+ debugs(20, DBG_IMPORTANT, "Could not parse mem-cached headers: " << e);
+ return false;
+ }
+ // local memory stores both headers and body
+ e.mem_obj->object_sz = sourceBuf.length; // from StoreEntry::complete()
+
+ storeGetMemSpace(sourceBuf.length); // from StoreEntry::write()
+
+ assert(e.mem_obj->data_hdr.write(sourceBuf)); // from MemObject::write()
+ const int64_t written = e.mem_obj->endOffset();
+ assert(written == sourceBuf.length); // StoreEntry::write never fails?
+ // would be nice to call validLength() here, but it needs e.key
+
+ debugs(20, 7, HERE << "mem-loaded all " << written << " bytes of " << e <<
+ " from " << page);
+
+ e.hideMemObject();
+
+ return true;
+}
+
+void
+MemStore::considerKeeping(StoreEntry &e)
+{
+ if (!e.memoryCachable()) {
+ debugs(20, 7, HERE << "Not memory cachable: " << e);
+ return; // cannot keep due to entry state or properties
+ }
+
+ assert(e.mem_obj);
+ if (!willFit(e.mem_obj->endOffset())) {
+ debugs(20, 5, HERE << "No mem-cache space for " << e);
+ return; // failed to free enough space
+ }
+
+ keep(e); // may still fail
+}
+
+bool
+MemStore::willFit(int64_t need)
+{
+ // TODO: obey configured maximum entry size (with page-based rounding)
+ return need <= Ipc::Mem::PageSize();
+}
+
+/// allocates map slot and calls copyToShm to store the entry in shared memory
+void
+MemStore::keep(StoreEntry &e)
+{
+ if (!map) {
+ debugs(20, 5, HERE << "No map to mem-cache " << e);
+ return;
+ }
+
+ sfileno index = 0;
+ Ipc::StoreMapSlot *slot = map->openForWriting(reinterpret_cast<const cache_key *>(e.key), index);
+ if (!slot) {
+ debugs(20, 5, HERE << "No room in mem-cache map to index " << e);
+ return;
+ }
+
+ MemStoreMap::Extras &extras = map->extras(index);
+ if (copyToShm(e, extras)) {
+ slot->set(e);
+ map->closeForWriting(index, false);
+ } else {
+ map->abortIo(index);
+ }
+}
+
+/// uses mem_hdr::copy() to copy local data to shared memory
+bool
+MemStore::copyToShm(StoreEntry &e, MemStoreMap::Extras &extras)
+{
+ Ipc::Mem::PageId page;
+ if (!Ipc::Mem::GetPage(page)) {
+ debugs(20, 5, HERE << "No mem-cache page for " << e);
+ return false; // GetPage is responsible for any cleanup on failures
+ }
+
+ const int64_t bufSize = Ipc::Mem::PageSize();
+ const int64_t eSize = e.mem_obj->endOffset();
+
+ StoreIOBuffer sharedSpace(bufSize, 0,
+ static_cast<char*>(PagePointer(page)));
+
+ // check that we kept everything or purge incomplete/sparse cached entry
+ const ssize_t copied = e.mem_obj->data_hdr.copy(sharedSpace);
+ if (eSize != copied) {
+ debugs(20, 2, HERE << "Failed to mem-cache " << e << ": " <<
+ eSize << "!=" << copied);
+ // cleanup
+ PutPage(page);
+ return false;
+ }
+
+ debugs(20, 7, HERE << "mem-cached all " << eSize << " bytes of " << e <<
+ " in " << page);
+
+ // remember storage location and size
+ extras.page = page;
+ extras.storedSize = copied;
+ return true;
+}
--- /dev/null
+/*
+ * $Id$
+ */
+
+#ifndef SQUID_MEMSTORE_H
+#define SQUID_MEMSTORE_H
+
+#include "Store.h"
+#include "MemStoreMap.h"
+
+/// Stores HTTP entities in RAM. Current implementation uses shared memory.
+/// Unlike a disk store (SwapDir), operations are synchronous (and fast).
+class MemStore: public Store {
+public:
+ MemStore();
+ virtual ~MemStore();
+
+ /// cache the entry or forget about it until the next considerKeeping call
+ void considerKeeping(StoreEntry &e);
+
+ /* Store API */
+ virtual int callback();
+ virtual StoreEntry * get(const cache_key *);
+ virtual void get(String const key , STOREGETCLIENT callback, void *cbdata);
+ virtual void init();
+ virtual uint64_t maxSize() const;
+ virtual uint64_t minSize() const;
+ virtual void stat(StoreEntry &) const;
+ virtual StoreSearch *search(String const url, HttpRequest *);
+ virtual void reference(StoreEntry &);
+ virtual void dereference(StoreEntry &);
+ virtual void maintain();
+ virtual void updateSize(int64_t size, int sign);
+
+protected:
+ bool willFit(int64_t needed);
+ void keep(StoreEntry &e);
+
+ bool copyToShm(StoreEntry &e, MemStoreMap::Extras &extras);
+ bool copyFromShm(StoreEntry &e, const MemStoreMap::Extras &extras);
+
+private:
+ MemStoreMap *map; ///< index of mem-cached entries
+};
+
+// Why use Store as a base? MemStore and SwapDir are both "caches".
+
+// Why not just use a SwapDir API? That would not help much because Store has
+// to check/update memory cache separately from the disk cache. And same API
+// would hurt because we can support synchronous get/put, unlike the disks.
+
+#endif /* SQUID_MEMSTORE_H */
--- /dev/null
+/*
+ * $Id$
+ *
+ * DEBUG: section 20 Memory Cache
+ */
+
+#include "config.h"
+
+#include "Store.h"
+#include "MemStoreMap.h"
+
+MemStoreMap::MemStoreMap(const char *const aPath, const int limit):
+ Ipc::StoreMap(aPath, limit, Shared::MemSize(limit))
+{
+ assert(shm.mem());
+ shared = new (shm.reserve(Shared::MemSize(limit))) Shared;
+}
+
+MemStoreMap::MemStoreMap(const char *const aPath):
+ Ipc::StoreMap(aPath)
+{
+ const int limit = entryLimit();
+ assert(shm.mem());
+ shared = reinterpret_cast<Shared *>(shm.reserve(Shared::MemSize(limit)));
+}
+
+MemStoreMap::Extras &
+MemStoreMap::extras(const sfileno fileno)
+{
+ assert(0 <= fileno && fileno < entryLimit());
+ assert(shared);
+ return shared->extras[fileno];
+}
+
+const MemStoreMap::Extras &
+MemStoreMap::extras(const sfileno fileno) const
+{
+ assert(0 <= fileno && fileno < entryLimit());
+ assert(shared);
+ return shared->extras[fileno];
+}
+
+size_t
+MemStoreMap::Shared::MemSize(int limit)
+{
+ return sizeof(Shared) + limit*sizeof(Extras);
+}
--- /dev/null
+#ifndef SQUID_MEMSTOREMAP_H
+#define SQUID_MEMSTOREMAP_H
+
+#include "ipc/StoreMap.h"
+#include "ipc/mem/Page.h"
+
+/// map of MemStore-cached entries
+class MemStoreMap: public Ipc::StoreMap
+{
+public:
+ // StoreEntry restoration info not already stored by Ipc::StoreMap
+ class Extras {
+ public:
+ Ipc::Mem::PageId page; ///< shared memory page with the entry content
+ int64_t storedSize; ///< total size of the stored entry content
+ };
+
+public:
+ MemStoreMap(const char *const aPath, const int limit); ///< create a new shared StoreMap
+ MemStoreMap(const char *const aPath); ///< open an existing shared StoreMap
+
+ /// write access to the extras; call openForWriting() first!
+ Extras &extras(const sfileno fileno);
+ /// read-only access to the extras; call openForReading() first!
+ const Extras &extras(const sfileno fileno) const;
+
+private:
+ /// data shared by all MemStoreMaps with the same path
+ class Shared {
+ public:
+ static size_t MemSize(int limit);
+ Extras extras[]; ///< extras storage
+ };
+
+ Shared *shared; ///< pointer to shared memory
+};
+
+#endif /* SQUID_MEMSTOREMAP_H */
void cacheNegatively(); /** \todo argh, why both? */
void invokeHandlers();
void purgeMem();
+ void cacheInMemory(); ///< start or continue storing in memory cache
void swapOut();
bool swapOutAble() const;
void swapOutFileClose(int how);
int checkNegativeHit() const;
int locked() const;
int validToSend() const;
- int keepInMemory() 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 *);
virtual RefCount<Store> store() const;
MemObject *mem_obj;
+ MemObject *hidden_mem_obj; ///< mem_obj created before URLs were known
RemovalPolicyNode repl;
/* START OF ON-DISK STORE_META_STD TLV field */
time_t timestamp;
virtual void maintain() = 0; /* perform regular maintenance should be private and self registered ... */
+ // XXX: This method belongs to Store::Root/StoreController, but it is here
+ // because test cases use non-StoreController derivatives as Root
+ /// called when the entry is no longer needed by any transaction
+ virtual void handleIdleEntry(StoreEntry &e) {}
+
/* These should really be private */
virtual void updateSize(int64_t size, int sign) = 0;
/* forward decls */
class RemovalPolicy;
+class MemStore;
/* Store dir configuration routines */
/* SwapDir *sd, char *path ( + char *opt later when the strtok mess is gone) */
class ConfigOption;
-/* New class that replaces the static SwapDir methods as part of the Store overhaul */
-
+/// hides memory/disk cache distinction from callers
class StoreController : public Store
{
virtual void get(String const, STOREGETCLIENT, void * cbdata);
+ /* Store parent API */
+ virtual void handleIdleEntry(StoreEntry &e);
+
virtual void init();
virtual void maintain(); /* perform regular maintenance should be private and self registered ... */
private:
void createOneStore(Store &aStore);
- StorePointer swapDir;
+ StorePointer swapDir; ///< summary view of all disk caches
+ MemStore *memStore; ///< memory cache
};
/* migrating from the Config based list of swapdirs */
SQUIDCEXTERN int storeDirGetBlkSize(const char *path, int *blksize);
SQUIDCEXTERN int storeDirGetUFSStats(const char *, int *, int *, int *, int *);
-
+/// manages a single cache_dir
class SwapDir : public Store
{
#include "StoreFileSystem.h"
#include "DiskIO/DiskIOModule.h"
#include "ipc/Kids.h"
+#include "ipc/mem/Pages.h"
#include "ipc/Coordinator.h"
#include "ipc/Strand.h"
#include "ip/tools.h"
/* NOTREACHED */
}
+ // XXX: this logic should probably be moved into Ipc::Mem::Init()
+ if (IamMasterProcess())
+ Ipc::Mem::Init();
+ else
+ if (UsingSmp() && IamWorkerProcess())
+ Ipc::Mem::Attach();
+
if (!opt_no_daemon && Config.workers > 0)
watch_child(argv);
* offset 0 in the memory object is the HTTP headers.
*/
+ if (/* XXX: restore: UsingSmp() && */ mem_status == IN_MEMORY) {
+ // clients of an object cached in shared memory are memory clients
+ return STORE_MEM_CLIENT;
+ }
+
+ assert(mem_obj);
+
if (mem_obj->inmem_lo)
return STORE_DISK_CLIENT;
}
StoreEntry::StoreEntry():
+ hidden_mem_obj(NULL),
swap_file_sz(0)
{
debugs(20, 3, HERE << "new StoreEntry " << this);
}
StoreEntry::StoreEntry(const char *aUrl, const char *aLogUrl):
+ hidden_mem_obj(NULL),
swap_file_sz(0)
{
debugs(20, 3, HERE << "new StoreEntry " << this);
SwapDir &sd = dynamic_cast<SwapDir&>(*store());
sd.disconnect(*this);
}
+ delete hidden_mem_obj;
}
void
MemObject *mem = mem_obj;
mem_obj = NULL;
delete mem;
+ delete hidden_mem_obj;
+ hidden_mem_obj = NULL;
+}
+
+void
+StoreEntry::hideMemObject()
+{
+ debugs(20, 3, HERE << "hiding " << mem_obj);
+ assert(mem_obj);
+ assert(!hidden_mem_obj);
+ hidden_mem_obj = mem_obj;
+ mem_obj = NULL;
}
void
return 0;
}
- // XXX: Rock store specific: since each SwapDir controls the index,
- // unlocked entries should not stay in the global store_table
- if (fileno >= 0) {
- Store::Root().dereference(*this);
- debugs(20, 5, HERE << "destroying unlocked entry: " << this << ' ' << *this);
- setMemStatus(NOT_IN_MEMORY);
- destroyStoreEntry(static_cast<hash_link *>(this));
- return 0;
- }
- else if (keepInMemory()) {
- Store::Root().dereference(*this);
- setMemStatus(IN_MEMORY);
- mem_obj->unlinkRequest();
- } else {
- Store::Root().dereference(*this);
-
- if (EBIT_TEST(flags, KEY_PRIVATE))
- debugs(20, 1, "WARNING: " << __FILE__ << ":" << __LINE__ << ": found KEY_PRIVATE");
-
- /* StoreEntry::purgeMem may free e */
- purgeMem();
- }
+ if (EBIT_TEST(flags, KEY_PRIVATE))
+ debugs(20, 1, "WARNING: " << __FILE__ << ":" << __LINE__ << ": found KEY_PRIVATE");
+ Store::Root().handleIdleEntry(*this); // may delete us
return 0;
}
}
}
+ // TODO: storeGetPublic() calls below may create unlocked entries.
+ // We should add/use storeHas() API or lock/unlock those entries.
if (mem_obj->vary_headers && !storeGetPublic(mem_obj->url, mem_obj->method)) {
/* Create "vary" base object */
String vary;
store_pages_max = Config.memMaxSize / sizeof(mem_node);
}
-int
-StoreEntry::keepInMemory() const
+bool
+StoreEntry::memoryCachable() const
{
if (mem_obj == NULL)
return 0;
if (!Config.onoff.memory_cache_first && swap_status == SWAPOUT_DONE && refcount == 1)
return 0;
- // already kept more than allowed
- if (mem_node::InUseCount() > store_pages_max)
+ const int64_t expectedSize = mem_obj->expectedReplySize();
+ // objects of unknown size are not allowed into the memory cache, for now
+ if (expectedSize < 0 || expectedSize > Config.Store.maxInMemObjSize)
return 0;
return 1;
if (new_status == mem_status)
return;
+ // XXX: restore: if (UsingSmp())
+ {
+ assert(new_status != IN_MEMORY); // we do not call this otherwise
+ // This method was designed to update replacement policy, not to
+ // actually purge something from the memory cache (TODO: rename?).
+ // Shared memory cache does not have a policy that needs updates.
+ mem_status = new_status;
+ return;
+ }
+
assert(mem_obj != NULL);
if (new_status == IN_MEMORY) {
debugs(20, 4, "StoreEntry::setMemStatus: inserted mem node " << mem_obj->url << " key: " << getMD5Text());
}
- hot_obj_count++;
+ hot_obj_count++; // TODO: maintain for the shared hot cache as well
} else {
if (EBIT_TEST(flags, ENTRY_SPECIAL)) {
debugs(20, 4, "StoreEntry::setMemStatus: special entry " << mem_obj->url);
if (mem_obj)
return;
+ if (hidden_mem_obj) {
+ debugs(20, 3, HERE << "restoring " << hidden_mem_obj);
+ mem_obj = hidden_mem_obj;
+ hidden_mem_obj = NULL;
+ mem_obj->resetUrls(aUrl, aLogUrl);
+ return;
+ }
+
mem_obj = new MemObject(aUrl, aLogUrl);
}
#include "squid.h"
#include "Store.h"
#include "MemObject.h"
+#include "MemStore.h"
+#include "mem_node.h"
#include "SquidMath.h"
#include "SquidTime.h"
#include "SwapDir.h"
int StoreController::store_dirs_rebuilding = 1;
StoreController::StoreController() : swapDir (new StoreHashIndex())
+ , memStore(NULL)
{}
StoreController::~StoreController()
-{}
+{
+ delete memStore;
+}
/*
* This function pointer is set according to 'store_dir_select_algorithm'
void
StoreController::init()
{
+ // XXX: add: if (UsingSmp())
+ memStore = new MemStore;
+ memStore->init();
+
swapDir->init();
if (0 == strcasecmp(Config.store_dir_select_algorithm, "round-robin")) {
void
StoreController::stat(StoreEntry &output) const
{
+ if (memStore)
+ memStore->stat(output);
+
storeAppendPrintf(&output, "Store Directory Statistics:\n");
storeAppendPrintf(&output, "Store Entries : %lu\n",
(unsigned long int)StoreEntry::inUseCount());
void
StoreController::sync(void)
{
- /* sync mem cache? */
+ if (memStore)
+ memStore->sync();
swapDir->sync();
}
if (e.swap_dirn > -1)
e.store()->reference(e);
- /* Notify the memory cache that we're referencing this object again */
+ // Notify the memory cache that we're referencing this object again
+ if (memStore && e.mem_status == IN_MEMORY)
+ memStore->reference(e);
+
+ // TODO: move this code to a non-shared memory cache class when we have it
if (e.mem_obj) {
if (mem_policy->Referenced)
mem_policy->Referenced(mem_policy, &e, &e.mem_obj->repl);
if (e.swap_filen > -1)
e.store()->dereference(e);
- /* Notify the memory cache that we're not referencing this object any more */
+ // Notify the memory cache that we're not referencing this object any more
+ if (memStore && e.mem_status == IN_MEMORY)
+ memStore->dereference(e);
+
+ // TODO: move this code to a non-shared memory cache class when we have it
if (e.mem_obj) {
if (mem_policy->Dereferenced)
mem_policy->Dereferenced(mem_policy, &e, &e.mem_obj->repl);
StoreController::get(const cache_key *key)
{
if (StoreEntry *e = swapDir->get(key)) {
+ // TODO: ignore and maybe handleIdleEntry() unlocked intransit entries
+ // because their backing store slot may be gone already.
debugs(20, 3, HERE << "got in-transit entry: " << *e);
return e;
}
+ if (memStore) {
+ if (StoreEntry *e = memStore->get(key)) {
+ debugs(20, 3, HERE << "got mem-cached entry: " << *e);
+ return e;
+ }
+ }
+
+ // TODO: this disk iteration is misplaced; move to StoreHashIndex
if (const int cacheDirs = Config.cacheSwap.n_configured) {
// ask each cache_dir until the entry is found; use static starting
// point to avoid asking the same subset of disks more often
fatal("not implemented");
}
+void
+StoreController::handleIdleEntry(StoreEntry &e)
+{
+ bool keepInLocalMemory = false;
+ if (memStore) {
+ memStore->considerKeeping(e);
+ // leave keepInLocalMemory false; memStore maintains its own cache
+ } else {
+ keepInLocalMemory = e.memoryCachable() && // entry is in good shape and
+ // the local memory cache is not overflowing
+ (mem_node::InUseCount() <= store_pages_max);
+ }
+
+ dereference(e);
+
+ // XXX: Rock store specific: Since each SwapDir controls its index,
+ // unlocked entries should not stay in the global store_table.
+ if (fileno >= 0) {
+ debugs(20, 5, HERE << "destroying unlocked entry: " << &e << ' ' << e);
+ destroyStoreEntry(static_cast<hash_link*>(&e));
+ return;
+ }
+
+ // TODO: move this into [non-shared] memory cache class when we have one
+ if (keepInLocalMemory) {
+ e.setMemStatus(IN_MEMORY);
+ e.mem_obj->unlinkRequest();
+ } else {
+ e.purgeMem(); // may free e
+ }
+}
+
StoreHashIndex::StoreHashIndex()
{
if (store_table)