BaseKillPlugin::KillResult BaseKillPlugin::resumeFromPrekillHook()

in src/oomd/plugins/BaseKillPlugin.cpp [111:213]


BaseKillPlugin::KillResult BaseKillPlugin::resumeFromPrekillHook(
    OomdContext& ctx) {
  OCHECK(prekill_hook_state_ != std::nullopt);

  if (prekill_hook_state_->hook_invocation->didFinish()) {
    OLOG << "pre-kill hook finished for Ruleset="
         << ctx.getActionContext().ruleset_name;
  } else if (pastPrekillHookTimeout(ctx)) {
    OLOG << "pre-kill hook timed out for Ruleset="
         << ctx.getActionContext().ruleset_name;
  } else {
    OLOG << "Still running pre-kill hook for Ruleset="
         << ctx.getActionContext().ruleset_name;
    return KillResult::DEFER;
  }

  auto deserialize_cgroup_ref = [&](const SerializedCgroupRef& sc)
      -> std::optional<OomdContext::ConstCgroupContextRef> {
    if (auto cgroup_ctx = ctx.addToCacheAndGet(sc.path)) {
      // Check inodes match and not a re-created cgroup. nullopt ids mean
      // deleted cgroups, which are never equal to each other.
      auto id = cgroup_ctx->get().id();
      if (id.has_value() && sc.id.has_value() && *id == *sc.id) {
        return cgroup_ctx;
      }
    }
    return std::nullopt;
  };

  // memoize deserialize_peer_group by serialized peer group pointer
  std::map<
      std::vector<SerializedCgroupRef>*,
      std::shared_ptr<std::vector<OomdContext::ConstCgroupContextRef>>>
      memoized_peer_groups;
  auto deserialize_peer_group =
      [&](std::vector<SerializedCgroupRef>* serialized_peers) {
        auto it = memoized_peer_groups.find(serialized_peers);
        if (it != memoized_peer_groups.end()) {
          return it->second;
        }
        auto deserialized_peers =
            std::make_shared<std::vector<OomdContext::ConstCgroupContextRef>>();
        for (const auto& peer : *serialized_peers) {
          if (auto peer_cgroup_ctx = deserialize_cgroup_ref(peer)) {
            deserialized_peers->emplace_back(*peer_cgroup_ctx);
          }
        }
        memoized_peer_groups[serialized_peers] = deserialized_peers;
        return deserialized_peers;
      };

  auto deserialize_kill_candidate =
      [&](const SerializedKillCandidate& skc) -> std::optional<KillCandidate> {
    if (auto candidate_ctx = deserialize_cgroup_ref(skc.target)) {
      if (auto kill_root_ctx = deserialize_cgroup_ref(skc.kill_root)) {
        return KillCandidate{
            .cgroup_ctx = *candidate_ctx,
            .kill_root = *kill_root_ctx,
            .peers = deserialize_peer_group(skc.peers.get())};
      }
    }

    return std::nullopt;
  };

  // pull state out of prekill_hook_state and clear it to delete the invocation
  auto intended_victim = std::move(prekill_hook_state_->intended_victim);
  auto serialized_next_best_option_stack =
      std::move(prekill_hook_state_->next_best_option_stack);
  prekill_hook_state_ = std::nullopt;

  // Try to kill intended victim
  if (auto intended_candidate = deserialize_kill_candidate(intended_victim)) {
    if (tryToLogAndKillCgroup(ctx, *intended_candidate)) {
      return KillResult::SUCCESS;
    }
  } else {
    // intended_candidate isn't deserializable means someone else removed it
    // before we could. Consider that they did our job for us. If we still
    // need to kill something detectors will fire again in the next interval and
    // start a fresh kill cycle.
    return KillResult::FAILED;
  }

  std::vector<KillCandidate> next_best_option_stack;
  for (const auto& skc : serialized_next_best_option_stack) {
    if (auto candidate = deserialize_kill_candidate(skc)) {
      next_best_option_stack.emplace_back(*candidate);
    } else {
      // candidate isn't deserializable means someone else killed it before we
      // got a chance to. resumeTryingToKillSomething gets to the point where it
      // would have killed candidate, consider the other folks to have done our
      // job for us. Don't keep going further down the DFS stack looking for
      // another cgroup to kill. This means removing all candidates *lower* in
      // the stack, or *earlier* in the vector.
      next_best_option_stack.clear();
    }
  }
  serialized_next_best_option_stack.clear();

  return resumeTryingToKillSomething(
      ctx, std::move(next_best_option_stack), true);
}