// Should be called outside m_lock void ConcurrentTableSharedStore::purgeExpired() { if (m_purgeCounter.fetch_add(1, std::memory_order_relaxed) % apcExtension::PurgeFrequency != 0) { return; } time_t now = time(nullptr); int64_t oldestLive = apcExtension::UseUncounted ? HPHP::Treadmill::getOldestStartTime() : 0; ExpirationPair tmp; int i = 0; while (apcExtension::PurgeRate < 0 || i < apcExtension::PurgeRate) { if (!m_expQueue.try_pop(tmp)) { break; } if (tmp.second > now) { m_expQueue.push(tmp); break; } if (apcExtension::UseFileStorage && strcmp(tmp.first, apcExtension::FileStorageFlagKey.c_str()) == 0) { s_apc_file_storage.adviseOut(); addToExpirationQueue(apcExtension::FileStorageFlagKey.c_str(), time(nullptr) + apcExtension::FileStorageAdviseOutPeriod); continue; } m_expMap.erase(tmp.first); eraseImpl(tmp.first, true, oldestLive); free((void *)tmp.first); ++i; } }
// Should be called outside m_lock void ConcurrentTableSharedStore::purgeExpired() { if ((atomic_add(m_purgeCounter, (uint64)1) % RuntimeOption::ApcPurgeFrequency) != 0) return; time_t now = time(NULL); ExpirationPair tmp; struct timespec tsBegin, tsEnd; gettime(CLOCK_MONOTONIC, &tsBegin); int i = 0; while (RuntimeOption::ApcPurgeRate < 0 || i < RuntimeOption::ApcPurgeRate) { if (!m_expQueue.try_pop(tmp)) { break; } if (tmp.second > now) { m_expQueue.push(tmp); break; } if (RuntimeOption::ApcUseFileStorage && strcmp(tmp.first, RuntimeOption::ApcFileStorageFlagKey.c_str()) == 0) { s_apc_file_storage.adviseOut(); addToExpirationQueue(RuntimeOption::ApcFileStorageFlagKey.c_str(), time(NULL) + RuntimeOption::ApcFileStorageAdviseOutPeriod); continue; } m_expMap.erase(tmp.first); eraseImpl(tmp.first, true); free((void *)tmp.first); ++i; } gettime(CLOCK_MONOTONIC, &tsEnd); int64 elapsed = gettime_diff_us(tsBegin, tsEnd); SharedStoreStats::addPurgingTime(elapsed); // Size could be inaccurate, but for stats reporting, it is good enough SharedStoreStats::setExpireQueueSize(m_expQueue.size()); }
bool ConcurrentTableSharedStore::get(const String& key, Variant& value) { const StoreValue *sval; APCHandle *svar = nullptr; ReadLock l(m_lock); bool expired = false; bool promoteObj = false; { Map::const_accessor acc; if (!m_vars.find(acc, tagStringData(key.get()))) { return false; } else { sval = &acc->second; if (sval->expired()) { // Because it only has a read lock on the data, deletion from // expiration has to happen after the lock is released expired = true; } else { if (auto const handle = sval->data.left()) { svar = handle; } else { std::lock_guard<SmallLock> sval_lock(sval->lock); if (auto const handle = sval->data.left()) { svar = handle; } else { /* * Note that unserialize can run arbitrary php code via a __wakeup * routine, which could try to access this same key, and we're * holding various locks here. This is only for promoting primed * values to in-memory values, so it's basically not a real * problem, but ... :) */ svar = unserialize(key, const_cast<StoreValue*>(sval)); if (!svar) return false; } } if (apcExtension::AllowObj && svar->type() == KindOfObject && !svar->objAttempted()) { // Hold ref here for later promoting the object svar->reference(); promoteObj = true; } value = svar->toLocal(); } } } if (expired) { eraseImpl(key, true, apcExtension::UseUncounted ? HPHP::Treadmill::getOldestStartTime() : 0); return false; } if (promoteObj) { handlePromoteObj(key, svar, value); // release the extra ref svar->unreference(); } return true; }
// Should be called outside m_lock void ConcurrentTableSharedStore::purgeExpired() { if (m_purgeCounter.fetch_add(1, std::memory_order_relaxed) % apcExtension::PurgeFrequency != 0) { return; } time_t now = time(nullptr); int64_t oldestLive = apcExtension::UseUncounted ? HPHP::Treadmill::getOldestStartTime() : 0; ExpirationPair tmp; int i = 0; while (apcExtension::PurgeRate < 0 || i < apcExtension::PurgeRate) { if (!m_expQueue.try_pop(tmp)) { break; } if (tmp.second > now) { m_expQueue.push(tmp); break; } if (UNLIKELY(tmp.first == intptr_t(apcExtension::FileStorageFlagKey.c_str()))) { adviseOut(); tmp.second = time(nullptr) + apcExtension::FileStorageAdviseOutPeriod; m_expQueue.push(tmp); continue; } ExpMap::accessor acc; if (m_expMap.find(acc, tmp.first)) { eraseImpl((char*)tmp.first, true, oldestLive, &acc); } ++i; } }
bool ConcurrentTableSharedStore::exists(CStrRef key) { const StoreValue *sval; ConditionalReadLock l(m_lock, !RuntimeOption::ApcConcurrentTableLockFree || m_lockingFlag); bool expired = false; { Map::const_accessor acc; if (!m_vars.find(acc, key.data())) { log_apc(std_apc_miss); return false; } else { sval = &acc->second; if (sval->expired()) { // Because it only has a read lock on the data, deletion from // expiration has to happen after the lock is released expired = true; } else { // No need toLocal() here, avoiding the copy if (sval->inMem()) { stats_on_get(key.get(), sval->var); } } } } if (expired) { log_apc(std_apc_miss); eraseImpl(key, true); return false; } log_apc(std_apc_hit); return true; }
bool ConcurrentTableSharedStore::exists(const String& keyStr) { const StoreValue *sval; ReadLock l(m_lock); bool expired = false; auto tag = tagStringData(keyStr.get()); { Map::const_accessor acc; if (!m_vars.find(acc, tag)) { return false; } else { sval = &acc->second; if (sval->expired()) { // Because it only has a read lock on the data, deletion from // expiration has to happen after the lock is released expired = true; } } } if (expired) { eraseImpl(tag, true, apcExtension::UseUncounted ? HPHP::Treadmill::getOldestStartTime() : 0, nullptr); return false; } return true; }
bool SharedStore::erase(CStrRef key, bool expired /* = false */) { bool success = eraseImpl(key, expired); if (RuntimeOption::EnableStats && RuntimeOption::EnableAPCStats) { ServerStats::Log(success ? "apc.erased" : "apc.erase", 1); } return success; }
bool ConcurrentTableSharedStore::get(const String& key, Variant &value) { const StoreValue *sval; APCHandle *svar = nullptr; ConditionalReadLock l(m_lock, !apcExtension::ConcurrentTableLockFree || m_lockingFlag); bool expired = false; bool promoteObj = false; { Map::const_accessor acc; if (!m_vars.find(acc, tagStringData(key.get()))) { log_apc(std_apc_miss); return false; } else { sval = &acc->second; if (sval->expired()) { // Because it only has a read lock on the data, deletion from // expiration has to happen after the lock is released expired = true; } else { if (!sval->inMem()) { std::lock_guard<SmallLock> sval_lock(sval->lock); if (!sval->inMem()) { svar = unserialize(key, sval); if (!svar) return false; } else { svar = sval->var; } } else { svar = sval->var; } if (apcExtension::AllowObj && svar->is(KindOfObject) && !svar->getObjAttempted()) { // Hold ref here for later promoting the object svar->incRef(); promoteObj = true; } value = svar->toLocal(); stats_on_get(key.get(), svar); } } } if (expired) { log_apc(std_apc_miss); eraseImpl(key, true); return false; } log_apc(std_apc_hit); if (promoteObj) { handlePromoteObj(key, svar, value); // release the extra ref svar->decRef(); } return true; }
bool InMemoryStorageFifo::evictItem() { if (!m_cleanupIndex.get<byArrival>().empty()) { CleanupIndex::index<byArrival>::type::iterator it = m_cleanupIndex.get<byArrival>().begin(); eraseImpl((*it)->getFullName()); m_cleanupIndex.get<byArrival>().erase(it); return true; } return false; }
bool InMemoryStorageLfu::evictItem() { if (!m_cleanupIndex.get<byFrequency>().empty()) { CleanupIndex::index<byFrequency>::type::iterator it = m_cleanupIndex.get<byFrequency>().begin(); eraseImpl(((*it).entry)->getFullName()); m_cleanupIndex.get<byFrequency>().erase(it); return true; } return false; }
bool ConcurrentTableSharedStore::exists(CStrRef key) { bool stats = RuntimeOption::EnableStats && RuntimeOption::EnableAPCStats; bool statsFetch = RuntimeOption::EnableAPCSizeStats && RuntimeOption::EnableAPCFetchStats; const StoreValue *val; ReadLock l(m_lock); bool expired = false; { Map::const_accessor acc; if (!m_vars.find(acc, key.data())) { if (stats) ServerStats::Log("apc.miss", 1); return false; } else { val = &acc->second; if (val->expired()) { // Because it only has a read lock on the data, deletion from // expiration has to happen after the lock is released expired = true; } else { // No need toLocal() here, avoiding the copy if (statsFetch) { SharedStoreStats::onGet(key.get(), val->var); } } } } if (expired) { if (stats) { ServerStats::Log("apc.miss", 1); } eraseImpl(key, true); return false; } if (stats) { ServerStats::Log("apc.hit", 1); } return true; }
// Should be called outside m_lock void ConcurrentTableSharedStore::purgeExpired() { if ((atomic_add(m_purgeCounter, (uint64)1) % RuntimeOption::ApcPurgeFrequency) != 0) return; time_t now = time(NULL); { // Check if there's work to do ReadLock lock(m_expirationQueueLock); if (m_expirationQueue.empty() || m_expirationQueue.top().second > now) { // No work return; } } // Purge items n at a time. The only operation under the write lock is // the pop #define PURGE_RATE 256 const char* s[PURGE_RATE]; while (true) { int i; { WriteLock lock(m_expirationQueueLock); const ExpirationPair *p = NULL; for (i = 0; i < PURGE_RATE && !m_expirationQueue.empty() && (p = &m_expirationQueue.top())->second < now; ++i, m_expirationQueue.pop()) { s[i] = p->first; } } for (int j = 0; j < i; ++j) { eraseImpl(s[j], true); free((void *)s[j]); } if (i < PURGE_RATE) { // No work left break; } } }
// Should be called outside m_lock void ConcurrentTableSharedStore::purgeExpired() { if (m_purgeCounter.fetch_add(1, std::memory_order_relaxed) % apcExtension::PurgeFrequency != 0) { return; } time_t now = time(nullptr); ExpirationPair tmp; struct timespec tsBegin, tsEnd; Timer::GetMonotonicTime(tsBegin); int i = 0; while (apcExtension::PurgeRate < 0 || i < apcExtension::PurgeRate) { if (!m_expQueue.try_pop(tmp)) { break; } if (tmp.second > now) { m_expQueue.push(tmp); break; } if (apcExtension::UseFileStorage && strcmp(tmp.first, apcExtension::FileStorageFlagKey.c_str()) == 0) { s_apc_file_storage.adviseOut(); addToExpirationQueue(apcExtension::FileStorageFlagKey.c_str(), time(nullptr) + apcExtension::FileStorageAdviseOutPeriod); continue; } m_expMap.erase(tmp.first); eraseImpl(tmp.first, true); free((void *)tmp.first); ++i; } Timer::GetMonotonicTime(tsEnd); int64_t elapsed = gettime_diff_us(tsBegin, tsEnd); SharedStoreStats::addPurgingTime(elapsed); // Size could be inaccurate, but for stats reporting, it is good enough SharedStoreStats::setExpireQueueSize(m_expQueue.size()); }
bool erase(const T_KeysTPL keysTuple) { return eraseImpl(keysTuple); }
bool ConcurrentTableSharedStore::eraseKey(const String& key) { assert(!key.isNull()); return eraseImpl(tagStringData(key.get()), false, 0, nullptr); }
bool ConcurrentTableSharedStore::get(CStrRef key, Variant &value) { bool stats = RuntimeOption::EnableStats && RuntimeOption::EnableAPCStats; bool statsFetch = RuntimeOption::EnableAPCSizeStats && RuntimeOption::EnableAPCFetchStats; const StoreValue *val; SharedVariant *svar = NULL; ReadLock l(m_lock); bool expired = false; { Map::const_accessor acc; if (!m_vars.find(acc, key.data())) { if (stats) ServerStats::Log("apc.miss", 1); return false; } else { val = &acc->second; if (val->expired()) { // Because it only has a read lock on the data, deletion from // expiration has to happen after the lock is released expired = true; } else { svar = val->var; if (RuntimeOption::ApcAllowObj) { // Hold ref here svar->incRef(); } value = svar->toLocal(); if (statsFetch) { SharedStoreStats::onGet(key.get(), svar); } } } } if (expired) { if (stats) { ServerStats::Log("apc.miss", 1); } eraseImpl(key, true); return false; } if (stats) { ServerStats::Log("apc.hit", 1); } if (RuntimeOption::ApcAllowObj) { bool statsDetail = RuntimeOption::EnableAPCSizeStats && RuntimeOption::EnableAPCSizeGroup; SharedVariant *converted = svar->convertObj(value); if (converted) { Map::accessor acc; m_vars.find(acc, key.data()); // start a write lock StoreValue *sval = &acc->second; SharedVariant *sv = sval->var; // sv may not be same as svar here because some other thread may have // updated it already, check before updating if (!sv->isUnserializedObj()) { if (statsDetail) { SharedStoreStats::onDelete(key.get(), sv, true); } sval->var = converted; sv->decRef(); if (RuntimeOption::EnableAPCSizeStats) { int32 newSize = converted->getSpaceUsage(); SharedStoreStats::updateDirect(sval->size, newSize); sval->size = newSize; } if (statsDetail) { int64 ttl = sval->expiry ? sval->expiry - time(NULL) : 0; SharedStoreStats::onStore(key.get(), converted, ttl, false); } } else { converted->decRef(); } } // release the extra ref svar->decRef(); } return true; }
bool ConcurrentTableSharedStore::erase(const String& key) { return eraseImpl(key, false, 0); }
bool ConcurrentTableSharedStore::erase(const String& key, bool expired /* = false */) { return eraseImpl(key, expired); }
/*************************************************************************** * erase ***************************************************************************/ bool erase(const T_Keys ...keys) { return eraseImpl(hana::make_tuple(keys...)); }