Esempio n. 1
0
CachedUnit lookupUnitNonRepoAuth(StringData* requestedPath,
                                 const struct stat& statInfo) {
  if (strstr(requestedPath->data(), "://") != nullptr) {
    // URL-based units are not currently cached in memory, but the Repo still
    // caches them on disk.
    return createUnitFromUrl(requestedPath);
  }

  // The string we're using as a key must be static, because we're using it as
  // a key in the cache (across requests).
  auto const path =
    makeStaticString(
      // XXX: it seems weird we have to do this even though we already ran
      // resolveVmInclude.
      (requestedPath->data()[0] == '/'
        ? requestedPath
        : String(SourceRootInfo::GetCurrentSourceRoot()) + StrNR(requestedPath)
      ).get()
    );

  NonRepoUnitCache::accessor acc;
  if (!s_nonRepoUnitCache.insert(acc, path)) {
    if (!isChanged(acc->second, statInfo)) {
      return acc->second.cachedUnit;
    }
  }

  /*
   * NB: the new-unit creation path is here, and is done while holding the tbb
   * lock on s_nonRepoUnitCache.  This was originally done deliberately to
   * avoid wasting time in the compiler (during server startup, many requests
   * hit the same code initial paths that are shared, and would all be
   * compiling the same files).  It's not 100% clear if this is the best way to
   * handle that idea, though (tbb locks spin aggressively and are expected to
   * be low contention).
   */

  /*
   * Don't cache if createNewUnit returns an empty CachedUnit---we'll need to
   * try again anyway if someone tries to load this path, since it might exist
   * later.
   *
   * If there was a unit for this path already, we need to put it on the
   * Treadmill for eventual reclaimation.  We can't delete it immediately
   * because other requests may still be using it.
   */
  auto const cu = createUnitFromFile(path);
  if (auto const oldUnit = acc->second.cachedUnit.unit) {
    Treadmill::enqueue([oldUnit] { reclaimUnit(oldUnit); });
  }
  acc->second.cachedUnit = cu;
  acc->second.mtime      = statInfo.st_mtim;
  acc->second.ino        = statInfo.st_ino;
  acc->second.devId      = statInfo.st_dev;
  return cu;
}
Esempio n. 2
0
CachedUnit loadUnitNonRepoAuth(StringData* requestedPath,
                               const struct stat* statInfo,
                               OptLog& ent,
                               const Native::FuncTable& nativeFuncs,
                               const RepoOptions& options,
                               FileLoadFlags& flags) {
  LogTimer loadTime("load_ms", ent);
  if (strstr(requestedPath->data(), "://") != nullptr) {
    // URL-based units are not currently cached in memory, but the Repo still
    // caches them on disk.
    return createUnitFromUrl(requestedPath, nativeFuncs, flags);
  }

  rqtrace::EventGuard trace{"WRITE_UNIT"};

  // The string we're using as a key must be static, because we're using it as
  // a key in the cache (across requests).
  auto const path =
    makeStaticString(
      // XXX: it seems weird we have to do this even though we already ran
      // resolveVmInclude.
      (FileUtil::isAbsolutePath(requestedPath->toCppString())
       ?  String{requestedPath}
        : String(SourceRootInfo::GetCurrentSourceRoot()) + StrNR(requestedPath)
      ).get()
    );

  auto const rpath = [&] () -> const StringData* {
    if (RuntimeOption::CheckSymLink) {
      std::string rp = StatCache::realpath(path->data());
      if (rp.size() != 0) {
        if (rp.size() != path->size() ||
            memcmp(rp.data(), path->data(), rp.size())) {
          return makeStaticString(rp);
        }
      }
    }
    return path;
  }();

  Stream::Wrapper* w = nullptr;
  auto& cache = getNonRepoCache(rpath, w);

  assertx(
    !w || &cache != &s_nonRepoUnitCache ||
    !RuntimeOption::EvalUnixServerQuarantineUnits
  );

  // Freeing a unit while holding the tbb lock would cause a rank violation when
  // recycle-tc is enabled as reclaiming dead functions requires that the code
  // and metadata locks be acquired.
  Unit* releaseUnit = nullptr;
  SCOPE_EXIT { if (releaseUnit) delete releaseUnit; };

  auto const updateAndUnlock = [] (auto& cachedUnit, auto p) {
    auto old = cachedUnit.update_and_unlock(std::move(p));
    if (old) {
      // We don't need to do anything explicitly; the copy_ptr
      // destructor will take care of it.
      Treadmill::enqueue([unit_to_delete = std::move(old)] () {});
    }
  };

  auto cuptr = [&] {
    NonRepoUnitCache::const_accessor rpathAcc;

    cache.insert(rpathAcc, rpath);
    auto& cachedUnit = rpathAcc->second.cachedUnit;
    if (auto const tmp = cachedUnit.copy()) {
      if (!isChanged(tmp, statInfo, options)) {
        flags = FileLoadFlags::kHitMem;
        if (ent) ent->setStr("type", "cache_hit_readlock");
        return tmp;
      }
    }

    cachedUnit.lock_for_update();
    try {
      if (auto const tmp = cachedUnit.copy()) {
        if (!isChanged(tmp, statInfo, options)) {
          cachedUnit.unlock();
          flags = FileLoadFlags::kWaited;
          if (ent) ent->setStr("type", "cache_hit_writelock");
          return tmp;
        }
        if (ent) ent->setStr("type", "cache_stale");
      } else {
        if (ent) ent->setStr("type", "cache_miss");
      }

      trace.finish();
      auto const cu = createUnitFromFile(rpath, &releaseUnit, w, ent,
                                         nativeFuncs, options, flags);
      auto const isICE = cu.unit && cu.unit->isICE();
      auto p = copy_ptr<CachedUnitWithFree>(cu, statInfo, isICE, options);
      // Don't cache the unit if it was created in response to an internal error
      // in ExternCompiler. Such units represent transient events.
      if (UNLIKELY(isICE)) {
        cachedUnit.unlock();
        return p;
      }
      updateAndUnlock(cachedUnit, p);
      return p;
    } catch (...) {
      cachedUnit.unlock();
      throw;
    }
  }();

  auto const ret = cuptr->cu;

  if (!ret.unit || !ret.unit->isICE()) {
    if (path != rpath) {
      NonRepoUnitCache::const_accessor pathAcc;
      cache.insert(pathAcc, path);
      if (pathAcc->second.cachedUnit.get().get() != cuptr) {
        auto& cachedUnit = pathAcc->second.cachedUnit;
        cachedUnit.lock_for_update();
        updateAndUnlock(cachedUnit, std::move(cuptr));
      }
    }
  }

  return ret;
}