예제 #1
0
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;
}
예제 #2
0
bool ConcurrentTableSharedStore::handlePromoteObj(const String& key,
                                                  APCHandle* svar,
                                                  const Variant& value) {
  auto const pair = APCObject::MakeAPCObject(svar, value);
  if (!pair.handle) return false;
  auto const converted = pair.handle;
  auto const size = pair.size;

  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(size);
    return false;
  }

  // Our handle may not be same as `svar' here because some other thread may
  // have updated it already, check before updating.
  auto& sval = acc->second;
  auto const handle = sval.data.left();
  if (handle == svar && handle->kind() == APCKind::SerializedObject) {
    sval.data = converted;
    APCStats::getAPCStats().updateAPCValue(
      converted, size, handle, sval.dataSize, sval.expire == 0, false);
    handle->unreferenceRoot(sval.dataSize);
    sval.dataSize = size;
    return true;
  }

  converted->unreferenceRoot(size);
  return false;
}
예제 #3
0
bool ConcurrentTableSharedStore::cas(const String& key, int64_t old,
                                     int64_t val) {
  ReadLock l(m_lock);

  Map::accessor acc;
  if (!m_vars.find(acc, tagStringData(key.get()))) {
    return false;
  }

  auto& sval = acc->second;
  if (sval.expired()) return false;

  auto const oldHandle = sval.data.match(
    [&] (APCHandle* h) {
      return h;
    },
    [&] (char* file) {
      return unserialize(key, &sval);
    }
  );
  if (!oldHandle || oldHandle->toLocal().toInt64() != old) {
    return false;
  }

  auto const pair = APCHandle::Create(Variant(val), false,
    APCHandleLevel::Outer, false);
  APCStats::getAPCStats().updateAPCValue(pair.handle, pair.size,
                                         oldHandle, sval.dataSize,
                                         sval.expire == 0, false);
  oldHandle->unreferenceRoot(sval.dataSize);
  sval.data = pair.handle;
  sval.dataSize = pair.size;
  return true;
}
예제 #4
0
int64_t ConcurrentTableSharedStore::inc(const String& key, int64_t step,
                                        bool& found) {
  found = false;
  ReadLock l(m_lock);

  Map::accessor acc;
  if (!m_vars.find(acc, tagStringData(key.get()))) {
    return 0;
  }
  auto& sval = acc->second;
  if (sval.expired()) return 0;

  /*
   * Inc only works on KindOfDouble or KindOfInt64, which are never kept in
   * file-backed storage from priming.  So we don't need to try to deserialize
   * anything or handle the case that sval.data is file-backed.
   */
  auto const oldHandle = sval.data.left();
  if (oldHandle == nullptr) return 0;
  if (oldHandle->type() != KindOfInt64 &&
      oldHandle->type() != KindOfDouble) {
    return 0;
  }

  auto const ret = oldHandle->toLocal().toInt64() + step;
  auto const pair = APCHandle::Create(Variant(ret), false);
  APCStats::getAPCStats().updateAPCValue(pair.handle, pair.size,
                                         oldHandle, sval.dataSize,
                                         sval.expire == 0, false);
  oldHandle->unreferenceRoot(sval.dataSize);
  sval.data = pair.handle;
  sval.dataSize = pair.size;
  found = true;
  return ret;
}
예제 #5
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;
}
예제 #6
0
/**
 * The Map::accessor here establishes a write lock, which means that other
 * threads, protected by read locks through Map::const_accessor, will not
 * read erased values from APC.
 * The ReadLock here is to sync with clear(), which only has a WriteLock,
 * not a specific accessor.
 */
bool ConcurrentTableSharedStore::eraseImpl(const String& key,
                                           bool expired,
                                           int64_t oldestLive) {
  if (key.isNull()) return false;
  ConditionalReadLock l(m_lock, !apcExtension::ConcurrentTableLockFree ||
                                m_lockingFlag);
  Map::accessor acc;
  if (m_vars.find(acc, tagStringData(key.get()))) {
    if (expired && !acc->second.expired()) {
      return false;
    }
    if (acc->second.inMem()) {
      if (expired && acc->second.expiry < oldestLive &&
          acc->second.var->getUncounted()) {
        APCTypedValue::fromHandle(acc->second.var)->deleteUncounted();
      } else {
        acc->second.var->unreferenceRoot();
      }
    } else {
      assert(acc->second.inFile());
      assert(acc->second.expiry == 0);
    }
    if (expired && acc->second.inFile()) {
      // a primed key expired, do not erase the table entry
      acc->second.var = nullptr;
      acc->second.size = 0;
      acc->second.expiry = 0;
    } else {
      eraseAcc(acc);
    }
    return true;
  }
  return false;
}
예제 #7
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;
}
예제 #8
0
bool ConcurrentTableSharedStore::exists(const String& key) {
  const StoreValue *sval;
  ConditionalReadLock l(m_lock, !apcExtension::ConcurrentTableLockFree ||
                                m_lockingFlag);
  bool expired = 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 {
        // 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;
}
예제 #9
0
/**
 * The Map::accessor here establishes a write lock, which means that other
 * threads, protected by read locks through Map::const_accessor, will not
 * read erased values from APC.
 * The ReadLock here is to sync with clear(), which only has a WriteLock,
 * not a specific accessor.
 */
bool ConcurrentTableSharedStore::eraseImpl(const String& key, bool expired) {
  if (key.isNull()) return false;
  ConditionalReadLock l(m_lock, !apcExtension::ConcurrentTableLockFree ||
                                m_lockingFlag);
  Map::accessor acc;
  if (m_vars.find(acc, tagStringData(key.get()))) {
    if (expired && !acc->second.expired()) {
      return false;
    }
    if (acc->second.inMem()) {
      stats_on_delete(key.get(), &acc->second, expired);
      acc->second.var->decRef();
    } else {
      assert(acc->second.inFile());
      assert(acc->second.expiry == 0);
    }
    if (expired && acc->second.inFile()) {
      // a primed key expired, do not erase the table entry
      acc->second.var = nullptr;
      acc->second.size = 0;
      acc->second.expiry = 0;
    } else {
      eraseAcc(acc);
    }
    return true;
  }
  return false;
}
예제 #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::cas(const String& key, int64_t old,
                                     int64_t val) {
  bool success = false;
  ConditionalReadLock l(m_lock, !apcExtension::ConcurrentTableLockFree ||
                                m_lockingFlag);
  StoreValue *sval;
  {
    Map::accessor acc;
    if (m_vars.find(acc, tagStringData(key.get()))) {
      sval = &acc->second;
      if (!sval->expired() && get_int64_value(sval) == old) {
        APCHandle *var = construct(Variant(val));
        sval->var->unreferenceRoot();
        sval->var = var;
        success = true;
      }
    }
  }
  return success;
}
예제 #12
0
int64_t ConcurrentTableSharedStore::inc(const String& key, int64_t step,
                                        bool &found) {
  found = false;
  int64_t ret = 0;
  ConditionalReadLock l(m_lock, !apcExtension::ConcurrentTableLockFree ||
                                m_lockingFlag);
  StoreValue *sval;
  {
    Map::accessor acc;
    if (m_vars.find(acc, tagStringData(key.get()))) {
      sval = &acc->second;
      if (!sval->expired()) {
        ret = get_int64_value(sval) + step;
        APCHandle *svar = construct(Variant(ret));
        sval->var->unreferenceRoot();
        sval->var = svar;
        found = true;
      }
    }
  }
  return ret;
}
예제 #13
0
/*
 * The Map::accessor here establishes a write lock, which means that other
 * threads, protected by read locks through Map::const_accessor, will not
 * read erased values from APC.
 *
 * The ReadLock here is to sync with clear(), which only has a WriteLock,
 * not a specific accessor.
 */
bool ConcurrentTableSharedStore::eraseImpl(const String& key,
        bool expired,
        int64_t oldestLive) {
    if (key.isNull()) return false;

    ReadLock l(m_lock);
    Map::accessor acc;
    if (!m_vars.find(acc, tagStringData(key.get()))) {
        return false;
    }
    if (expired && !acc->second.expired()) {
        return false;
    }

    auto& storeVal = acc->second;

    storeVal.data.match(
    [&] (APCHandle* var) {
        APCStats::getAPCStats().removeAPCValue(storeVal.dataSize, var,
                                               storeVal.expire == 0, expired);
        if (expired && storeVal.expire < oldestLive && var->isUncounted()) {
            APCTypedValue::fromHandle(var)->deleteUncounted();
        } else {
            var->unreferenceRoot(storeVal.dataSize);
        }

        eraseAcc(acc);
    },
    [&] (char* file) {
        assert(!expired);  // primed keys never say true to expired()
        eraseAcc(acc);
    }
    );

    return true;
}
예제 #14
0
bool ConcurrentTableSharedStore::eraseKey(const String& key) {
  assert(!key.isNull());
  return eraseImpl(tagStringData(key.get()), false, 0, nullptr);
}