blob: 50fd1e96e015adc6a6c31e4b7568bec30e975bf1 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <lib/fdio/fd.h>
#include <lib/zx/channel.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include <atomic>
#include <memory>
#include <thread>
// This is a test executable to demonstrate that we can tear down a process
// cleanly even with background threads blocked in |accept|.
int main(int argc, char** argv) {
zx::channel handle(zx_take_startup_handle(PA_HND(PA_USER0, 0)));
if (!handle.is_valid()) {
printf("failed to take startup handle\n");
return 1;
}
int fd;
zx_status_t status = fdio_fd_create(handle.get(), &fd);
if (status != ZX_OK) {
printf("failed to create file descriptor: %s\n", zx_status_get_string(status));
return 1;
}
struct FDErrno {
int FD;
int Errno;
};
const auto ret = std::make_shared<std::atomic<FDErrno>>();
std::thread child([fd, ret]() {
int rv = accept(fd, nullptr, nullptr);
// We should be blocked here. The FD table should have an entry reserved
// for the socket we are accepting.
ret->store({
.FD = rv,
.Errno = errno,
});
});
// At this point, the child thread should spin up and get blocked in accept
// waiting for the fake netstack to provide a socket. We need to simulate
// enough of the netstack to leave that thread blocked in accept and also
// unwind this process cleanly. This machinery is in the server.
// We need to wait for the accept call to come in.
status = handle.wait_one(ZX_USER_SIGNAL_0, zx::time::infinite(), nullptr);
if (status != ZX_OK) {
printf("failed to wait for accept call: %s\n", zx_status_get_string(status));
return 1;
}
status = handle.signal_peer(0, ZX_USER_SIGNAL_0);
if (status != ZX_OK) {
printf("failed to signal peer: %s\n", zx_status_get_string(status));
return 1;
}
child.detach();
// At this point, we have the process in the state we want, with a reserved
// entry in the FD table. We now want to unwind the process to prove that
// we can cleanly unwind a process with a reserved entry in its FD table.
//
// To unwind cleanly, we implement Close on the server, which will be called
// by the atexit logic, which would otherwise block.
//
// Now we try to unwind the process cleanly while the child thread is
// blocked in accept. The test passes if we do not crash while exiting
// the process.
FDErrno rv = ret->load();
if (!(rv.FD == 0 && rv.Errno == 0)) {
printf("failed to block in accept: %s (rv=%d)\n", strerror(rv.Errno), rv.FD);
return 1;
}
return 0;
}