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);
}