Fix a real race: -d checked is_network_in_use() before async cleanup ran
Even after fixing is_network_in_use()'s own exit-code bug, the real device still failed the same way: both managed networks reported "still in use" milliseconds after their only session was confirmed stopped. Root cause: kill_session() only waits for the *sandboxed process itself* (via its cgroup) to die -- it says nothing about the separate, independently scheduled daemonized process that started it, which still has its own post-waitpid() cleanup left to run (stop_tap_relay() among it) before a tap+relay join's host-side device is actually detached from the bridge. Checking is_network_in_use() the instant kill_session() returns can genuinely still see it attached; on the real device, the kill+check pair landed single-digit milliseconds apart in the log. network_becomes_unused() retries is_network_in_use() for up to 10s (nanosleep()-based, matching this project's existing polling style) instead of giving up on the first still-attached answer. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gv3s5jckJKzh6JkMoi2Akz
This commit is contained in:
+30
-1
@@ -25,6 +25,7 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -728,6 +729,34 @@ int compose_up_command(const std::string& images_directory, const std::string& c
|
|||||||
// state file (no -u/--up ever ran for this file) isn't an error -- just
|
// state file (no -u/--up ever ran for this file) isn't an error -- just
|
||||||
// nothing to stop.
|
// nothing to stop.
|
||||||
//
|
//
|
||||||
|
// A killed session's own sandboxed process (what kill_session() actually
|
||||||
|
// waits for, via the cgroup/pid-namespace/tracked-pid strategy it picks)
|
||||||
|
// isn't the same process as the one that started it: run_mounted_container()
|
||||||
|
// runs in the compose service's own daemonized process, blocked in its own
|
||||||
|
// waitpid() on bwrap -- once that returns, *that* process still has to run
|
||||||
|
// its own post-exit cleanup (stop_tap_relay() among it) before a tap+relay
|
||||||
|
// join's own host-side device is actually removed from the bridge. Since
|
||||||
|
// this happens in a completely separate, independently-scheduled process,
|
||||||
|
// checking is_network_in_use() the instant kill_session() itself returns
|
||||||
|
// can genuinely still see it as attached -- confirmed on the real Android
|
||||||
|
// target device, where the two kill+check calls above landed only single-
|
||||||
|
// digit milliseconds apart, well within the gap that process's own
|
||||||
|
// scheduling can take on a loaded device. This retries for a bounded time
|
||||||
|
// (nanosleep()-based, matching this project's own existing polling style)
|
||||||
|
// rather than giving up on the very first still-attached answer.
|
||||||
|
bool network_becomes_unused(const NetworkEntry& network, int timeout_ms, int interval_ms) {
|
||||||
|
for (int waited = 0; waited < timeout_ms; waited += interval_ms) {
|
||||||
|
if (!is_network_in_use(network)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
struct timespec ts {
|
||||||
|
interval_ms / 1000, (interval_ms % 1000) * 1000000L
|
||||||
|
};
|
||||||
|
nanosleep(&ts, nullptr);
|
||||||
|
}
|
||||||
|
return !is_network_in_use(network);
|
||||||
|
}
|
||||||
|
|
||||||
// Networks and volumes are then handled asymmetrically, matching real
|
// Networks and volumes are then handled asymmetrically, matching real
|
||||||
// Compose's own default and the user's own explicit direction: volumes
|
// Compose's own default and the user's own explicit direction: volumes
|
||||||
// always persist (never touched here at all -- a compose-managed volume
|
// always persist (never touched here at all -- a compose-managed volume
|
||||||
@@ -768,7 +797,7 @@ int compose_down_command(const std::string& compose_file_name, AppConfig& config
|
|||||||
if (it == config.networks.end()) {
|
if (it == config.networks.end()) {
|
||||||
continue; // never created, or already removed
|
continue; // never created, or already removed
|
||||||
}
|
}
|
||||||
if (is_network_in_use(*it)) {
|
if (!network_becomes_unused(*it, 10000, 200)) {
|
||||||
fmt::print("network '{}' is still in use, leaving it\n", actual_name);
|
fmt::print("network '{}' is still in use, leaving it\n", actual_name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user