예제 #1
0
APCHandle* APCArray::MakeShared(ArrayData* arr,
                                size_t& size,
                                bool inner,
                                bool unserializeObj) {
  if (!inner) {
    // only need to call traverseData() on the toplevel array
    DataWalker walker(DataWalker::LookupFeature::HasObjectOrResource);
    DataWalker::DataFeature features = walker.traverseData(arr);
    if (features.isCircular() || features.hasCollection()) {
      String s = apc_serialize(arr);
      APCHandle* handle = APCString::MakeShared(KindOfArray, s.get(), size);
      handle->setSerializedArray();
      return handle;
    }

    if (apcExtension::UseUncounted &&
        !features.hasObjectOrResource() &&
        !arr->empty()) {
      size = getMemSize(arr) + sizeof(APCTypedValue);
      return APCTypedValue::MakeSharedArray(arr);
    }
  }

  if (arr->isVectorData()) {
    return APCArray::MakePackedShared(arr, size, unserializeObj);
  }

  return APCArray::MakeShared(arr, size, unserializeObj);
}
예제 #2
0
파일: apc-array.cpp 프로젝트: 2bj/hhvm
APCHandle* APCArray::MakePackedShared(ArrayData* arr,
                                      bool unserializeObj) {
  size_t num_elems = arr->size();
  void* p = malloc(sizeof(APCArray) + sizeof(APCHandle*) * num_elems);
  auto ret = new (p) APCArray(static_cast<size_t>(num_elems));

  try {
    size_t i = 0;
    for (ArrayIter it(arr); !it.end(); it.next()) {
      APCHandle* val = APCHandle::Create(it.secondRef(),
                                         false, true,
                                         unserializeObj);
      if (val->shouldCache()) {
        ret->mustCache();
      }
      ret->vals()[i++] = val;
    }
    assert(i == num_elems);
  } catch (...) {
    delete ret;
    throw;
  }

  return ret->getHandle();
}
예제 #3
0
bool ConcurrentTableSharedStore::store(const String& key, const Variant& value,
                                       int64_t ttl,
                                       bool overwrite /* = true */,
                                       bool limit_ttl /* = true */) {
  StoreValue *sval;
  APCHandle* svar = construct(value);
  ConditionalReadLock l(m_lock, !apcExtension::ConcurrentTableLockFree ||
                                m_lockingFlag);
  const char *kcp = strdup(key.data());
  bool present;
  time_t expiry = 0;
  bool overwritePrime = false;
  {
    Map::accessor acc;
    present = !m_vars.insert(acc, kcp);
    sval = &acc->second;
    if (present) {
      free((void *)kcp);
      if (overwrite || sval->expired()) {
        // if ApcTTLLimit is set, then only primed keys can have expiry == 0
        overwritePrime = (sval->expiry == 0);
        if (sval->inMem()) {
          sval->var->unreferenceRoot();
        } else {
          // mark the inFile copy invalid since we are updating the key
          sval->sAddr = nullptr;
          sval->sSize = 0;
        }
      } else {
        svar->unreferenceRoot();
        return false;
      }
    }
    int64_t adjustedTtl = adjust_ttl(ttl, overwritePrime || !limit_ttl);
    if (check_noTTL(key.data(), key.size())) {
      adjustedTtl = 0;
    }
    sval->set(svar, adjustedTtl);
    expiry = sval->expiry;
  }
  if (expiry) {
    addToExpirationQueue(key.data(), expiry);
  }
  if (apcExtension::ExpireOnSets) {
    purgeExpired();
  }
  return true;
}
예제 #4
0
APCHandle* APCObject::MakeAPCObject(APCHandle* obj, CVarRef value) {
  if (!value.is(KindOfObject) || obj->getObjAttempted()) {
    return nullptr;
  }
  obj->setObjAttempted();
  ObjectData *o = value.getObjectData();
  DataWalker walker(DataWalker::LookupFeature::DetectSerializable);
  DataWalker::DataFeature features = walker.traverseData(o);
  if (features.isCircular() ||
      features.hasCollection() ||
      features.hasSerializableReference()) {
    return nullptr;
  }
  APCHandle* tmp = APCHandle::Create(value, false, true, true);
  tmp->setObjAttempted();
  return tmp;
}
예제 #5
0
APCHandle* APCObject::MakeAPCObject(APCHandle* obj, CVarRef value) {
    if (!value.is(KindOfObject) || obj->getObjAttempted()) {
        return nullptr;
    }
    obj->setObjAttempted();
    ObjectData *o = value.getObjectData();
    if (o->instanceof(SystemLib::s_SerializableClass)) {
        // should also check the object itself
        return nullptr;
    }
    PointerSet seen;
    if (o->hasInternalReference(seen, true)) {
        return nullptr;
    }
    APCHandle* tmp = APCHandle::Create(value, false, true, true);
    tmp->setObjAttempted();
    return tmp;
}
예제 #6
0
APCHandle* APCObject::MakeAPCObject(
    APCHandle* obj, size_t& size, const Variant& value) {
  if (!value.is(KindOfObject) || obj->objAttempted()) {
    return nullptr;
  }
  obj->setObjAttempted();
  ObjectData *o = value.getObjectData();
  if (apcExtension::OptimizeSerialization) {
    return MakeShared(o, size, false, false);
  }
  DataWalker walker(DataWalker::LookupFeature::DetectSerializable);
  DataWalker::DataFeature features = walker.traverseData(o);
  if (features.isCircular() ||
      features.hasCollection() ||
      features.hasSerializableReference()) {
    return nullptr;
  }
  APCHandle* tmp = APCHandle::Create(value, size, false, true, true);
  tmp->setObjAttempted();
  return tmp;
}
예제 #7
0
bool ConcurrentTableSharedStore::handlePromoteObj(const String& key,
                                                  APCHandle* svar,
                                                  const Variant& value) {
  APCHandle *converted = APCObject::MakeAPCObject(svar, value);
  if (converted) {
    Map::accessor acc;
    if (!m_vars.find(acc, tagStringData(key.get()))) {
      // There is a chance another thread deletes the key when this thread is
      // converting the object. In that case, we just bail
      converted->unreferenceRoot();
      return false;
    }
    // A write lock was acquired during find
    StoreValue *sval = &acc->second;
    APCHandle *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 == svar && !sv->getIsObj()) {
      sval->var = converted;
      sv->unreferenceRoot();
      return true;
    }
    converted->unreferenceRoot();
  }
  return false;
}
예제 #8
0
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;
}
예제 #9
0
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);
}
예제 #10
0
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;
}
예제 #11
0
bool ConcurrentTableSharedStore::storeImpl(const String& key,
                                           const Variant& value,
                                           int64_t ttl,
                                           bool overwrite,
                                           bool limit_ttl) {
  StoreValue *sval;
  auto svar = APCHandle::Create(value, false, APCHandleLevel::Outer, false);
  auto keyLen = key.size();
  ReadLock l(m_lock);
  char* const kcp = strdup(key.data());
  bool present;
  time_t expiry = 0;
  bool overwritePrime = false;
  {
    Map::accessor acc;
    APCHandle* current = nullptr;
    present = !m_vars.insert(acc, kcp);
    sval = &acc->second;
    if (present) {
      free(kcp);
      if (!overwrite && !sval->expired()) {
        svar.handle->unreferenceRoot(svar.size);
        return false;
      }
      sval->data.match(
        [&] (APCHandle* handle) {
          current = handle;
          // If ApcTTLLimit is set, then only primed keys can have
          // expire == 0.
          overwritePrime = sval->expire == 0;
        },
        [&] (char*) {
          // Was inFile, but won't be anymore.
          sval->data = nullptr;
          sval->dataSize = 0;
          overwritePrime = true;
        }
      );
    } else {
      APCStats::getAPCStats().addKey(keyLen);
    }

    int64_t adjustedTtl = adjust_ttl(ttl, overwritePrime || !limit_ttl);
    if (check_noTTL(key.data(), key.size())) {
      adjustedTtl = 0;
    }

    if (current) {
      if (sval->expire == 0 && adjustedTtl != 0) {
        APCStats::getAPCStats().removeAPCValue(
          sval->dataSize, current, true, sval->expired());
        APCStats::getAPCStats().addAPCValue(svar.handle, svar.size, false);
      } else {
        APCStats::getAPCStats().updateAPCValue(
          svar.handle, svar.size, current, sval->dataSize,
          sval->expire == 0, sval->expired());
      }
      current->unreferenceRoot(sval->dataSize);
    } else {
      APCStats::getAPCStats().addAPCValue(svar.handle, svar.size, present);
    }

    sval->set(svar.handle, adjustedTtl);
    sval->dataSize = svar.size;
    expiry = sval->expire;
    if (expiry) {
      auto ikey = intptr_t(acc->first);
      if (m_expMap.insert({ ikey, 0 })) {
        m_expQueue.push({ ikey, expiry });
      }
    }
  }

  if (apcExtension::ExpireOnSets) {
    purgeExpired();
  }

  return true;
}
예제 #12
0
bool ConcurrentTableSharedStore::store(const String& key, CVarRef value,
                                       int64_t ttl,
                                       bool overwrite /* = true */,
                                       bool limit_ttl /* = true */) {
  StoreValue *sval;
  APCHandle* svar = construct(value);
  ConditionalReadLock l(m_lock, !apcExtension::ConcurrentTableLockFree ||
                                m_lockingFlag);
  const char *kcp = strdup(key.data());
  bool present;
  time_t expiry = 0;
  bool overwritePrime = false;
  {
    Map::accessor acc;
    present = !m_vars.insert(acc, kcp);
    sval = &acc->second;
    bool update = false;
    if (present) {
      free((void *)kcp);
      if (overwrite || sval->expired()) {
        // if ApcTTLLimit is set, then only primed keys can have expiry == 0
        overwritePrime = (sval->expiry == 0);
        if (sval->inMem()) {
          stats_on_update(key.get(), sval, svar,
                          adjust_ttl(ttl, overwritePrime || !limit_ttl));
          sval->var->decRef();
          update = true;
        } else {
          // mark the inFile copy invalid since we are updating the key
          sval->sAddr = nullptr;
          sval->sSize = 0;
        }
      } else {
        svar->decRef();
        return false;
      }
    }
    int64_t adjustedTtl = adjust_ttl(ttl, overwritePrime || !limit_ttl);
    if (check_noTTL(key.data(), key.size())) {
      adjustedTtl = 0;
    }
    sval->set(svar, adjustedTtl);
    expiry = sval->expiry;
    if (!update) {
      stats_on_add(key.get(), sval, adjustedTtl, false, false);
    }
  }
  if (expiry) {
    addToExpirationQueue(key.data(), expiry);
  }
  if (apcExtension::ExpireOnSets) {
    purgeExpired();
  }
  if (present) {
    log_apc(std_apc_update);
  } else {
    log_apc(std_apc_new);
    if (RuntimeOption::EnableStats && RuntimeOption::EnableAPCKeyStats) {
      string prefix = "apc.new." + GetSkeleton(key);
      ServerStats::Log(prefix, 1);
    }
  }
  return true;
}