Starting execution threads, with either
    x::run() or by
    using std::thread is generally mutually-exclusive
    with a multi-process based approach that uses fork().
    This is because another execution thread could be in the middle of
    using some
    mutex or a condition variable,
    at fork() time.
    This results in a child process inheriting a potentially inconsistent
    internal contents of the mutex or a condition variable.
  
    It is safe to fork() and immediately
    exec() something else.
    x::forkexec
    provides a convenient way to implement this.
    Here's an example. This is not really the
    best way to read the contents of
      a directory, this is just an example of using
    x::forkexec;
  
#include <x/forkexec.H> if (x::forkexec("ls", "-al").system() != 0) { std::cout << "Where did ls go?" << std::endl; }
    x::forkexec itself is just a container for an
    external executable's command line parameters and other attributes.
    The constructor takes a variadic list of strings,
    naming the program and any arguments. Alternatively,
    the constructor can also take a single
    std::vector<std::string>, which cannot
    be empty.
  
    system() forks and has the child process
    exec the program, then waits for the child process to exit.
    This is exactly equivalent to:
  
#include <x/forkexec.H> x::forkexec lsal("ls", "-al"); pid_t p=lsal.spawn(); if (x::forkexec::wait4(p) != 0) { std::cout << "Where did ls go?" << std::endl; }
    system() is equivalent to a
    spawn(), which forks and execs the child process
    without waiting for it to terminate,
    followed by wait4().
  
    spawn_detached() is an alternative to
    spawn() that forks twice before execing, with
    the first child process exiting, leaving the parent process without
    a child process.
    init becomes the exec'd process's parent.
  
    The name of the process to execute gets taken from the first argument
    vector string. program overrides it:
  
#include <x/forkexec.H> if (x::forkexec("-sh").program("/bin/bash").system() != 0) { std::cout << "Where did ls go?" << std::endl; }
    This executes /bin/bash, but it gets
    “-sh” as its argv[0].
  
    In all cases, the child process's filename can have an explicit path,
    or use PATH to find it.
  
    If the process cannot be executed for some reason,
    system(),
    spawn(), or
    spawn_detached() throws an
    exception.
    There's also an exec() method, which execs the
    current process. Either it succeeds, and never returns, or throws an
    exception.
  
#include <x/forkexec.H> x::forkexec wc("wc", "-l"); x::fd wcstdin = wc.pipe_to(); x::fd wcstdout = wc.pipe_from(); pid_t p=wc.spawn(); // ...
      After constructing an x::forkexec but
      before starting the process, pipe_to() makes
      arrangements to attach a pipe to the new process's standard input,
      and returns the write side of the pipe.
      Similarly, pipe_from() attaches a pipe to the
      new process's standard output, and returns the read side of the pipe.
    
      pipe_from() and
      pipe_to() take an optional
      int argument, naming
      an explicit file descriptor, rather than standard input or output.
      There's also a socket_fd(),
      that creates a bidirectional pipe socket,
      attaches one end of it to the new process's file descriptor,
      then returns the other end of the socket pipe.
      Finally, set_fd() takes a numerical file
      descriptor number, and an x::fd that you
      created, and attaches it to the new process's file descriptor
      (when it actually gets started).