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; }
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; }
CVarRef APCLocalArray::getValueRef(ssize_t pos) const { APCHandle *sv = m_arr->getValue(pos); DataType t = sv->getType(); if (!IS_REFCOUNTED_TYPE(t)) { return APCTypedValue::fromHandle(sv)->asCVarRef(); } if (LIKELY(m_localCache != nullptr)) { assert(unsigned(pos) < m_arr->capacity()); TypedValue* tv = &m_localCache[pos]; if (tv->m_type != KindOfUninit) { return tvAsCVarRef(tv); } } else { static_assert(KindOfUninit == 0, "must be 0 since we use smart_calloc"); unsigned cap = m_arr->capacity(); m_localCache = (TypedValue*) smart_calloc(cap, sizeof(TypedValue)); } TypedValue* tv = &m_localCache[pos]; tvAsVariant(tv) = sv->toLocal(); assert(tv->m_type != KindOfUninit); return tvAsCVarRef(tv); }