On 1/9/20 12:12 PM, George Dunlap wrote: > libxl needs to be able to know when processes it forks have completed. > > At the moment, libxl has two basic mode (with some variations). In > one mode -- libxl_sigchld_owner_libxl* -- libxl sets up its own > SIGCHLD signal handler, and also handles the event loop that allows > libxl to safely block until the child in question is finished (using a > self-pipe for the SIGCHLD handler to notify the waiters that it's time > to look for reaped children). > > In the other mode, libxl does not set up the SIGCHLD handler, nor does > it do anything with processing the event loop; it expects the library > caller to handle the event loop itself. > > The golang runtime manages its own processes, and thus must use > SIGCHLD itself; and it has an easy way for other users to get SIGCHLD > notifications. However, because its event loop is hidden away behind > abstractions, it's not easy to hook into; and there's no need -- the > golang runtime assumes that C function calls may block, and handles > everything behind the scenes.
FWIW, attached is a C program that behaves as I think golang would behave, which hangs in `mainloop` mode. It's actually got two modes: Run it without any arguments, and it will run in "default" mode (not setting up a SIGCHLD nor setting childproc_mode); run with at least one argument (whatever it is), it will run in `mainloop` mode, and hang after the second SIGCHLD event. -George
#include <unistd.h> #include <stdlib.h> #include <string.h> #include <libxl.h> #include <assert.h> #include <pthread.h> #include <poll.h> #include <signal.h> #include <fcntl.h> char * malloc_copy(const char *src) { int len = strlen(src); char * dst = malloc(strlen(src)+1); strncpy(dst, src, len); return dst; } libxl_ctx *ctx; int self_pipe_wakeup(int fd) { /* Called from signal handlers, so needs to be async-signal-safe */ static const char buf[1] = ""; for (;;) { int r = write(fd, buf, 1); if (r==1) return 0; assert(r==-1); if (errno == EINTR) continue; if (errno == EWOULDBLOCK) return 0; if (!errno) abort(); return errno; } } int self_pipe_eatall(int fd) { char buf[256]; for (;;) { int r = read(fd, buf, sizeof(buf)); if (r == sizeof(buf)) continue; if (r >= 0) return 0; assert(r == -1); if (errno == EINTR) continue; if (errno == EWOULDBLOCK) return 0; assert(errno); return errno; } } int sigchld_selfpipe[2]; static void *sigchld_helper(void *fd) { struct pollfd pfd; int r; pfd.fd = (int)(unsigned long)fd; pfd.events = POLLIN | POLLHUP; while (true) { // Wait for a sigchld on [0] fprintf(stderr, "Waiting for self-pipe\n"); r = poll(&pfd, 1, -1); if (r < 0) { if (errno == EINTR) continue; perror("poll"); return NULL; } if (pfd.revents == POLLHUP) { fprintf(stderr, "Received POLLHUP, closing helper\n"); return NULL; } fprintf(stderr, "Self-pipe received, calling libxl_childproc_sigchld_occurred\n"); self_pipe_eatall(pfd.fd); libxl_childproc_sigchld_occurred(ctx); } return NULL; } static void sigchld_handler(int signo) { // Signal on pipe [1] fprintf(stderr, "SIGCHLD received, self-signaling\n"); self_pipe_wakeup(sigchld_selfpipe[1]); } void setup_sigchld(void) { struct sigaction ours, old; pthread_t t; int i, flags, r; // Get self-signal pipe if (pipe(sigchld_selfpipe) < 0) { perror("Failed to create a pipe"); exit(1); } r = pthread_create(&t, NULL, sigchld_helper, (void *)(unsigned long)sigchld_selfpipe[0]); if (r < 0) { perror("Creating helper thread"); exit(1); } // Make non-blocking for (i = 0; i < 2; i++) { int fd = sigchld_selfpipe[i]; flags = fcntl(fd, F_GETFL); if (flags == -1) { perror("Getting block flags"); exit(1); } flags |= O_NONBLOCK; r = fcntl(fd, F_SETFL, flags); if (flags == -1) { perror("Setting pipe non-blocking"); exit(1); } } memset(&ours,0,sizeof(ours)); ours.sa_handler = sigchld_handler; sigemptyset(&ours.sa_mask); ours.sa_flags = SA_NOCLDSTOP | SA_RESTART; r = sigaction(SIGCHLD, &ours, &old); assert(!r); } int main(int argc, char * argv[]) { int rc; xentoollog_logger_stdiostream * xtl; libxl_domain_config cconfig; uint32_t domid; bool mainloop = false; if (argc > 1) mainloop=true; if (mainloop) setup_sigchld(); xtl = xtl_createlogger_stdiostream(stderr, XTL_DEBUG, 0); rc = libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, (xentoollog_logger*)xtl); if (rc) { fprintf(stderr, "Getting libxl_ctx: %d", rc); exit(1); } if (mainloop) { libxl_childproc_hooks *cp = malloc(sizeof(*cp)); cp->chldowner = libxl_sigchld_owner_mainloop; libxl_childproc_setmode(ctx, cp, NULL); } libxl_domain_config_init(&cconfig); libxl_domain_build_info_init_type(&cconfig.b_info, LIBXL_DOMAIN_TYPE_PV); cconfig.c_info.type = LIBXL_DOMAIN_TYPE_PV; cconfig.c_info.name = malloc_copy("c6-01"); cconfig.b_info.max_vcpus = 4; cconfig.b_info.max_memkb = 2048*1024; cconfig.b_info.target_memkb = 2048*1024; cconfig.on_crash = LIBXL_ACTION_ON_SHUTDOWN_DESTROY; cconfig.b_info.bootloader = malloc_copy("pygrub"); cconfig.num_disks = 1; cconfig.disks = malloc(sizeof(libxl_device_disk)); libxl_device_disk_init(cconfig.disks); cconfig.disks[0].vdev = malloc_copy("hda"); cconfig.disks[0].format = LIBXL_DISK_FORMAT_RAW; cconfig.disks[0].pdev_path = malloc_copy("/images/c6-01.raw"); cconfig.disks[0].readwrite = 1; cconfig.num_nics = 1; cconfig.nics = malloc(sizeof(libxl_device_nic)); libxl_device_nic_init(cconfig.nics); rc = libxl_domain_create_new(ctx, &cconfig, &domid, NULL, NULL); if (rc) { fprintf(stderr, "Creating domain: %d", rc); exit(1); } printf("Created domain with id %d", domid); libxl_domain_config_dispose(&cconfig); if (mainloop) { close(sigchld_selfpipe[1]); close(sigchld_selfpipe[0]); } return 0; }
_______________________________________________ Xen-devel mailing list Xen-devel@lists.xenproject.org https://lists.xenproject.org/mailman/listinfo/xen-devel