[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH 16/20] libxl: event API: new facilities for waiting for subprocesses



On Fri, 2012-03-16 at 16:26 +0000, Ian Jackson wrote:
> The current arrangements in libxl for spawning subprocesses have two
> key problems: (i) they make unwarranted (and largely undocumented)
> assumptions about the caller's use of subprocesses, (ii) they aren't
> integrated into the event system and can't be made asynchronous etc.
> 
> So replace them with a new set of facilities.
> 
> Primarily, from the point of view of code inside libxl, this is
> libxl__ev_child_fork which is both (a) a version of fork() and (b) an
> event source which generates a callback when the child dies.
> 
> From the point of view of the application, we fully document our use
> of SIGCHLD.  The application can tell us whether it wants to own
> SIGCHLD or not; if it does, it has to tell us about deaths of our
> children.
> 
> Currently there are no callers in libxl which use these facilities.
> All code in libxl which forks needs to be converted and libxl_fork
> needse to be be abolished.
> 
> Signed-off-by: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
> Cc: Roger Pau Monne <roger.pau@xxxxxxxxxxxxx>
> ---
>  tools/libxl/libxl.c          |   17 +++-
>  tools/libxl/libxl_event.c    |   52 +++++++--
>  tools/libxl/libxl_event.h    |  142 ++++++++++++++++++++++++-
>  tools/libxl/libxl_fork.c     |  243 
> ++++++++++++++++++++++++++++++++++++++++++
>  tools/libxl/libxl_internal.h |   61 ++++++++++-
>  5 files changed, 495 insertions(+), 20 deletions(-)
> 
> diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
> index 469f66a..ee184ca 100644
> --- a/tools/libxl/libxl.c
> +++ b/tools/libxl/libxl.c
> @@ -39,7 +39,7 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version,
>      memset(ctx, 0, sizeof(libxl_ctx));
>      ctx->lg = lg;
> 
> -    /* First initialise pointers (cannot fail) */
> +    /* First initialise pointers etc. (cannot fail) */
> 
>      LIBXL_TAILQ_INIT(&ctx->occurred);
> 
> @@ -58,6 +58,11 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version,
>      LIBXL_TAILQ_INIT(&ctx->death_list);
>      libxl__ev_xswatch_init(&ctx->death_watch);
> 
> +    ctx->childproc_hooks = &libxl__childproc_default_hooks;
> +    ctx->childproc_user = 0;
> +
> +    ctx->sigchld_selfpipe[0] = -1;
> +
>      /* The mutex is special because we can't idempotently destroy it */
> 
>      if (libxl__init_recursive_mutex(ctx, &ctx->lock) < 0) {
> @@ -160,6 +165,16 @@ int libxl_ctx_free(libxl_ctx *ctx)
> 
>      discard_events(&ctx->occurred);
> 
> +    /* If we have outstanding children, then the application inherits
> +     * them; we wish the application good luck with understanding
> +     * this if and when it reaps them. */

:-)

> +    libxl__sigchld_removehandler(ctx);
> +
> +    if (ctx->sigchld_selfpipe[0] >= 0) {
> +        close(ctx->sigchld_selfpipe[0]);
> +        close(ctx->sigchld_selfpipe[1]);
> +    }
> +
>      pthread_mutex_destroy(&ctx->lock);
> 
>      GC_FREE;
> diff --git a/tools/libxl/libxl_event.c b/tools/libxl/libxl_event.c
> index 5405299..dcd5802 100644
> --- a/tools/libxl/libxl_event.c
> +++ b/tools/libxl/libxl_event.c
> @@ -620,6 +620,10 @@ static int beforepoll_internal(libxl__gc *gc, 
> libxl__poller *poller,
>                                                                         \
>          REQUIRE_FD(poller->wakeup_pipe[0], POLLIN, BODY);              \
>                                                                         \
> +        int selfpipe = libxl__fork_selfpipe_active(CTX);               \
> +        if (selfpipe >= 0)                                             \
> +            REQUIRE_FD(selfpipe, POLLIN, BODY);                        \
> +                                                                       \
>      }while(0)
> 
>  #define REQUIRE_FD(req_fd_, req_events_, BODY) do{      \
> @@ -749,10 +753,11 @@ static void afterpoll_internal(libxl__egc *egc, 
> libxl__poller *poller,
>                                 int nfds, const struct pollfd *fds,
>                                 struct timeval now)
>  {
> +    /* May make callbacks into the appliction for child processes.

Typo:                                application

> +     * ctx must be locked exactly once */
>      EGC_GC;
>      libxl__ev_fd *efd;
> 
> -
>      LIBXL_LIST_FOREACH(efd, &CTX->efds, entry) {
>          if (!efd->events)
>              continue;
> @@ -763,11 +768,16 @@ static void afterpoll_internal(libxl__egc *egc, 
> libxl__poller *poller,
>      }
> 
>      if (afterpoll_check_fd(poller,fds,nfds, poller->wakeup_pipe[0],POLLIN)) {
> -        char buf[256];
> -        int r = read(poller->wakeup_pipe[0], buf, sizeof(buf));
> -        if (r < 0)
> -            if (errno != EINTR && errno != EWOULDBLOCK)
> -                LIBXL__EVENT_DISASTER(egc, "read wakeup", errno, 0);
> +        int e = libxl__self_pipe_eatall(poller->wakeup_pipe[0]);
> +        if (e) LIBXL__EVENT_DISASTER(egc, "read wakeup", e, 0);
> +    }
> +
> +    int selfpipe = libxl__fork_selfpipe_active(CTX);
> +    if (selfpipe >= 0 &&
> +        afterpoll_check_fd(poller,fds,nfds, selfpipe, POLLIN)) {
> +        int e = libxl__self_pipe_eatall(selfpipe);
> +        if (e) LIBXL__EVENT_DISASTER(egc, "read sigchld pipe", e, 0);
> +        libxl__fork_selfpipe_woken(egc);
>      }
> 
>      for (;;) {
> @@ -1063,16 +1073,36 @@ void libxl__poller_put(libxl_ctx *ctx, libxl__poller 
> *p)
> 
>  void libxl__poller_wakeup(libxl__egc *egc, libxl__poller *p)
>  {
> +    int e = libxl__self_pipe_wakeup(p->wakeup_pipe[1]);
> +    if (e) LIBXL__EVENT_DISASTER(egc, "cannot poke watch pipe", e, 0);
> +}
> +
> +int libxl__self_pipe_wakeup(int fd)
> +{
>      static const char buf[1] = "";
> 
>      for (;;) {
> -        int r = write(p->wakeup_pipe[1], buf, 1);
> -        if (r==1) return;
> +        int r = write(fd, buf, 1);
> +        if (r==1) return 0;
>          assert(r==-1);
>          if (errno == EINTR) continue;
> -        if (errno == EWOULDBLOCK) return;
> -        LIBXL__EVENT_DISASTER(egc, "cannot poke watch pipe", errno, 0);
> -        return;
> +        if (errno == EWOULDBLOCK) return 0;
> +        assert(errno);
> +        return errno;
> +    }
> +}
> +
> +int libxl__self_pipe_eatall(int fd)
> +{
> +    char buf[256];
> +    for (;;) {
> +        int r = read(fd, buf, sizeof(buf));
> +        if (r >= 0) return 0;

Is there some 256 byte limit on the number of bytes pending in the pipe
somewhere? Wouldn't r==256 require us to (potentially) go round the loop
again?

> +        assert(r == -1);
> +        if (errno == EINTR) continue;
> +        if (errno == EWOULDBLOCK) return 0;
> +        assert(errno);
> +        return errno;
>      }
>  }
> 
> diff --git a/tools/libxl/libxl_event.h b/tools/libxl/libxl_event.h
> index 41aebc0..26c33eb 100644
> --- a/tools/libxl/libxl_event.h
> +++ b/tools/libxl/libxl_event.h
> @@ -163,11 +163,6 @@ void libxl_event_register_callbacks(libxl_ctx *ctx,
>   * After libxl_ctx_free, all corresponding evgen handles become
>   * invalid and must no longer be passed to evdisable.
>   *
> - * Events enabled with evenable prior to a fork and libxl_ctx_postfork
> - * are no longer generated after the fork/postfork; however the evgen
> - * structures are still valid and must be passed to evdisable if the
> - * memory they use should not be leaked.
> - *
>   * Applications should ensure that they eventually retrieve every
>   * event using libxl_event_check or libxl_event_wait, since events
>   * which occur but are not retreived by the application will be queued
> @@ -372,6 +367,142 @@ void libxl_osevent_occurred_fd(libxl_ctx *ctx, void 
> *for_libxl,
>  void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl);
> 
> 
> +/*======================================================================*/
> +
> +/*
> + * Subprocess handling.
> + *
> + * Unfortunately the POSIX interface makes this very awkward.

To say the least...

> + *
> + * There are two possible arrangements for collecting statuses from
> + * wait/waitpid.
> + *
> + * For naive programs:
> + *
> + *     libxl will keep a SIGCHLD handler installed whenever it has an
> + *     active (unreaped) child.  It will reap all children with
> + *     wait(); any children it does not recognise will be passed to
> + *     the application via an optional callback (and will result in
> + *     logged warnings if no callback is provided or the callback
> + *     denies responsibility for the child).
> + *
> + *     libxl may have children whenever:
> + *
> + *       - libxl is performing an operation which can be made
> + *         asynchronous; ie one taking a libxl_asyncop_how, even
> + *         if NULL is passed indicating that the operation is
> + *         synchronous; or
> + *
> + *       - events of any kind are being generated, as requested
> + *         by libxl_evenable_....
> + *
> + *     A multithreaded application which is naive in this sense may
> + *     block SIGCHLD on some of its threads, but there must be at
> + *     least one thread that has SIGCHLD unblocked.  libxl will not
> + *     modify the blocking flag for SIGCHLD (except that it may create
> + *     internal service threads with all signals blocked).
> + *
> + *     A naive program must only have at any one time only
> + *     one libxl context which might have children.

> + * For programs which run their own children alongside libxl's:
> + *
> + *     The application must install a SIGCHLD handler and reap (at
> + *     least) all of libxl's children and pass their exit status
> + *     to libxl by calling libxl_childproc_exited.
> + *
> + *     An application which does this must call
> + *     libxl_childproc_setmode.
> + *
> + * An application which fails to call setmode, or which passes 0 for
> + * hooks, while it uses any libxl operation which might
> + * create or use child processes (see above):
> + *   - Must not have any child processes running.
> + *   - Must not install a SIGCHLD handler.
> + *   - Must not reap any children.
> + */
> +
> +
> +typedef enum {
> +    /* libxl owns SIGCHLD whenever it has a child. */
> +    libxl_sigchld_owner_libxl,
> +
> +    /* Application promises to call libxl_childproc_exited but NOT
> +     * from within a signal handler.  libxl will not itself arrange to
> +     * (un)block or catch SIGCHLD. */
> +    libxl_sigchld_owner_mainloop,
> +
> +    /* libxl owns SIGCHLD all the time, and the application is
> +     * relying on libxl's event loop for reaping its own children. */
> +    libxl_sigchld_owner_libxl_always,

The comment above doesn't seem to cover this third case?

> +} libxl_sigchld_owner;
> +
> +typedef struct {
> +    libxl_sigchld_owner chldowner;
> +
> +    /* All of these are optional: */
> +
> +    /* Called by libxl instead of fork.  Should behave exactly like
> +     * fork, including setting errno etc.  May NOT reenter into libxl.
> +     * Application may use this to discover pids of libxl's children,
> +     * for example.
> +     */
> +    pid_t (*fork_replacement)(void *user);
> +
> +    /* With libxl_sigchld_owner_libxl, called by libxl when it has
> +     * reaped a pid.  (Not permitted with _owner_mainloop.)
> +     *
> +     * Should return 0 if the child was recognised by the application
> +     * (or if the application does not keep those kind of records),
> +     * ERROR_NOT_READY if the application knows that the child is not

ERROR_NOT_READY seems like an odd name. ERROR_UNKNOWN_CHILD perhaps?

> +     * the application's; if it returns another error code it is a
> +     * disaster as described for libxl_event_register_callbacks.
> +     * (libxl will report unexpected children to its error log.)
> +     *
> +     * If not supplied, the application is assumed not to start
> +     * any children of its own.
> +     *
> +     * This function is NOT called from within the signal handler.
> +     * Rather it will be called from inside a libxl's event handling
> +     * code and thus only when libxl is running, for example from
> +     * within libxl_event_wait.  (libxl uses the self-pipe trick
> +     * to implement this.)
> +     *
> +     * childproc_exited_callback may call back into libxl, but it
> +     * is best to avoid making long-running libxl calls as that might
> +     * stall the calling event loop while the nested operation
> +     * completes.
> +     */
> +    int (*reaped_callback)(pid_t, int status, void *user);
> +} libxl_childproc_hooks;
> +
> +/* hooks may be 0 in which is equivalent to &{ libxl_sigchld_owner_libxl, 0, 
> 0 }
> + *
> + * May not be called when libxl might have any child processes, or the
> + * behaviour is undefined.  So it is best to call this at
> + * initialisation.
> + */
> +void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks 
> *hooks,
> +                             void *user);
> +
> +/*
> + * This function is for an application which owns SIGCHLD and which
> + * therefore reaps all of the process's children.
> + *
> + * May be called only by an application which has called setmode with
> + * chldowner == libxl_sigchld_owner_mainloop.  If pid was a process started
> + * by this instance of libxl, returns 0 after doing whatever
> + * processing is appropriate.  Otherwise silently returns
> + * ERROR_NOT_READY.  No other error returns are possible.
> + *
> + * May NOT be called from within a signal handler which might
> + * interrupt any libxl operation.  The application will almost
> + * certainly need to use the self-pipe trick (or a working pselect or
> + * ppoll) to implement this.
> + */
> +int libxl_childproc_reaped(libxl_ctx *ctx, pid_t, int status);
> +
> +
>  /*
>   * An application which initialises a libxl_ctx in a parent process
>   * and then forks a child which does not quickly exec, must
> @@ -383,6 +514,7 @@ void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void 
> *for_libxl);
>  void libxl_postfork_child_noexec(libxl_ctx *ctx);
> 
> 
> +

The blank lines appear to be breeding ;-)

>  #endif
> 
>  /*
> diff --git a/tools/libxl/libxl_fork.c b/tools/libxl/libxl_fork.c
> index 4aaa0b5..ee62b61 100644
> --- a/tools/libxl/libxl_fork.c
> +++ b/tools/libxl/libxl_fork.c
> @@ -46,6 +46,12 @@ static int atfork_registered;
>  static LIBXL_LIST_HEAD(, libxl__carefd) carefds =
>      LIBXL_LIST_HEAD_INITIALIZER(carefds);
> 
> +/* non-null iff installed, protected by no_forking */
> +static libxl_ctx *sigchld_owner;
> +static struct sigaction sigchld_saved_action;
> +
> +static void sigchld_removehandler_core(void);
> +
>  static void atfork_lock(void)
>  {
[...]
> @@ -117,6 +124,10 @@ void libxl_postfork_child_noexec(libxl_ctx *ctx)
>          free(cf);
>      }
>      LIBXL_LIST_INIT(&carefds);
> +
> +    if (sigchld_owner)
> +        sigchld_removehandler_core();
> +
>      atfork_unlock();
>  }
> 
> @@ -138,6 +149,238 @@ int libxl__carefd_fd(const libxl__carefd *cf)
[...]
> +
> +int libxl__sigchld_installhandler(libxl_ctx *ctx) /* non-reentrant */
> +{
> +    int r, rc;
> +
> +    if (ctx->sigchld_selfpipe[0] < 0) {
> +        r = pipe(ctx->sigchld_selfpipe);
> +        if (r) {
> +            ctx->sigchld_selfpipe[0] = -1;
> +            LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR,
> +                             "failed to create sigchld pipe");
> +            rc = ERROR_FAIL;
> +            goto out;
> +        }
> +    }
> +
> +    atfork_lock();
> +    if (sigchld_owner != ctx) {

A non-NULL sigchld_owner which is != ctx is a pretty serious error here,
isn't it?

> +        struct sigaction ours;
> +
> +        assert(!sigchld_owner);
> +        sigchld_owner = ctx;
> +
> +        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, &sigchld_saved_action);
> +        assert(!r);
> +
> +        assert(((void)"application must negotiate with libxl about SIGCHLD",
> +                !(sigchld_saved_action.sa_flags & SA_SIGINFO) &&
> +                (sigchld_saved_action.sa_handler == SIG_DFL ||
> +                 sigchld_saved_action.sa_handler == SIG_IGN)));
> +    }
> +    atfork_unlock();
> +
> +    rc = 0;
> + out:
> +    return rc;
> +}
[...]
> +void libxl__fork_selfpipe_woken(libxl__egc *egc) {
> +    /* May make callbacks into the appliction for child processes.

                                      application

> +     * ctx must be locked EXACTLY ONCE */
> +    EGC_GC;
> +
> +    while (chldmode_ours(CTX) /* in case the app changes the mode */) {

setmode takes the CTX lock and ctx must be locked here too, so can this
happen? Ah, we unlock inside the loop, ok then.

Changing the mode in the callback seems, er, ambitious given you can't
call it when libxl has any children and we recommend you do it once at
start of day. Can we not just outlaw it?

> +        int status;
> +        pid_t pid = waitpid(-1, &status, WNOHANG);
> +
> +        if (pid == 0) return;
> +
> +        if (pid == -1) {
> +            if (errno == ECHILD) return;
> +            if (errno == EINTR) continue;
> +            LIBXL__EVENT_DISASTER(egc, "waitpid() failed", errno, 0);
> +            return;
> +        }
> +
> +        int rc = childproc_reaped(egc, pid, status);
> +
> +        if (rc) {
> +            if (CTX->childproc_hooks->reaped_callback) {
> +                CTX_UNLOCK;
> +                CTX->childproc_hooks->reaped_callback
> +                    (pid, status, CTX->childproc_user);
> +                CTX_LOCK;
> +            } else {
> +                libxl_report_child_exitstatus(CTX, XTL_WARN,
> +                                "unknown child", (long)pid, status);
> +            }
> +        }
> +    }
> +}
> +
> +pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child *ch,
> +                           libxl__ev_child_callback *death)
> +{
> +    CTX_LOCK;
> +    int rc;
> +
> +    if (chldmode_ours(CTX)) {
> +        rc = libxl__sigchld_installhandler(CTX);
> +        if (rc) goto out;
> +    }
> +
> +    pid_t pid =
> +        CTX->childproc_hooks->fork_replacement
> +        ? CTX->childproc_hooks->fork_replacement(CTX->childproc_user)
> +        : fork();
> +    if (pid == -1) {
> +        LOGE(ERROR, "fork failed");
> +        rc = ERROR_FAIL;
> +        goto out;
> +    }
> +
> +    if (!pid) {
> +        /* woohoo! */

\o/

> +        return 0; /* Yes, CTX is left locked in the child. */
> +    }
> +
> +    ch->pid = pid;
> +    ch->callback = death;
> +    LIBXL_LIST_INSERT_HEAD(&CTX->children, ch, entry);
> +    rc = pid;
> +
> + out:
> +    perhaps_removehandler(CTX);
> +    CTX_UNLOCK;
> +    return rc;
> +}
> +
> +void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks 
> *hooks,
> +                             void *user)
> +{
> +    GC_INIT(ctx);
> +    CTX_LOCK;
> +
> +    assert(LIBXL_LIST_EMPTY(&CTX->children));
> +
> +    if (!hooks)
> +        hooks = &libxl__childproc_default_hooks;
           user = NULL;

(probably doesn't matter much)

> +
> +    ctx->childproc_hooks = hooks;
> +    ctx->childproc_user = user;
> +
> +    switch (ctx->childproc_hooks->chldowner) {
> +    case libxl_sigchld_owner_mainloop:
> +    case libxl_sigchld_owner_libxl:
> +        libxl__sigchld_removehandler(ctx);
> +        break;
> +    case libxl_sigchld_owner_libxl_always:
> +        libxl__sigchld_installhandler(ctx);
> +        break;
> +    default:
> +        abort();
> +    }
> +
> +    CTX_UNLOCK;
> +    GC_FREE;
> +}
> +
> +const libxl_childproc_hooks libxl__childproc_default_hooks = {
> +    libxl_sigchld_owner_libxl, 0, 0
> +};
> +
> +/*
>   * Local variables:
>   * mode: C
>   * c-basic-offset: 4
> diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
> index 983de3e..0a344cf 100644
> --- a/tools/libxl/libxl_internal.h
> +++ b/tools/libxl/libxl_internal.h
> @@ -184,6 +184,19 @@ typedef struct libxl__ev_watch_slot {
>  libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc, int slotnum);
> 
> 
> +typedef struct libxl__ev_child libxl__ev_child;
> +typedef void libxl__ev_child_callback(libxl__egc *egc, libxl__ev_child*,
> +                                      pid_t pid, int status);
> +struct libxl__ev_child {
> +    /* caller should include this in their own struct */
> +    /* read-only for caller: */
> +    pid_t pid; /* -1 means unused ("unregistered", ie Idle) */
> +    libxl__ev_child_callback *callback;
> +    /* remainder is private for libxl__ev_... */
> +    LIBXL_LIST_ENTRY(struct libxl__ev_child) entry;
> +};
> +
> +
>  /*
>   * evgen structures, which are the state we use for generating
>   * events for the caller.
> @@ -263,6 +276,8 @@ struct libxl__ctx {
>      const libxl_event_hooks *event_hooks;
>      void *event_hooks_user;
> 
> +    LIBXL_LIST_ENTRY(struct libxl__ctx);

Shouldn't there be a field name here? Or is it deliberately anon? (what
if we get a second list)

> +
>      pthread_mutex_t lock; /* protects data structures hanging off the ctx */
>        /* Always use libxl__ctx_lock and _unlock (or the convenience
>         * macors CTX_LOCK and CTX_UNLOCK) to manipulate this.
> @@ -303,10 +318,14 @@ struct libxl__ctx {
> 
>      LIBXL_LIST_HEAD(, libxl_evgen_disk_eject) disk_eject_evgens;
> 
> -    /* for callers who reap children willy-nilly; caller must only
> -     * set this after libxl_init and before any other call - or
> -     * may leave them untouched */
> +    const libxl_childproc_hooks *childproc_hooks;
> +    void *childproc_user;
> +    int sigchld_selfpipe[2]; /* [0]==-1 means handler not installed */
> +    LIBXL_LIST_HEAD(, libxl__ev_child) children;
> +
> +    /* This is obsolete and must be removed: */
>      int (*waitpid_instead)(pid_t pid, int *status, int flags);
> +
>      libxl_version_info version_info;
>  };
> 
> @@ -554,6 +573,33 @@ static inline int libxl__ev_xswatch_isregistered(const 
> libxl__ev_xswatch *xw)
> 
> 
>  /*
> + * For making subprocesses.  This is the only permitted mechanism for
> + * code in libxl to do so.
> + *
> + * In the parent, returns the pid, filling in childw_out.
> + * In the child, returns 0.
> + * If it fails, returns a libxl error (all of which are -ve).
> + *
> + * The child should go on to exec (or exit) soon, and should not make
> + * any further libxl event calls in the meantime.
> + *
> + * The parent may signal the child but it must not reap it.  That will
> + * be done by the event machinery.  Either death or cldstop may be

"cldstop" did you rename that childw_out and miss this comment or is it
something else?

> + * NULL in which case that kind of event is ignored.
> + *
> + * It is not possible to "deregister" the child death event source.
> + * It will generate exactly one event callback; until then the childw
> + * is Active and may not be reused.
> + */
> +_hidden pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child 
> *childw_out,
> +                                 libxl__ev_child_callback *death);
> +static inline void libxl__ev_child_init(libxl__ev_child *childw_out)
> +                { childw_out->pid = -1; }
> +static inline int libxl__ev_child_inuse(libxl__ev_child *childw_out)
> +                { return childw_out->pid >= 0; }
> +
> +
> +/*
>   * Other event-handling support provided by the libxl event core to
>   * the rest of libxl.
>   */
> @@ -607,6 +653,15 @@ void libxl__poller_put(libxl_ctx *ctx, libxl__poller *p);
>   * ctx must be locked. */
>  void libxl__poller_wakeup(libxl__egc *egc, libxl__poller *p);
> 
> +/* Internal to fork and child reaping machinery */
> +extern const libxl_childproc_hooks libxl__childproc_default_hooks;
> +int libxl__sigchld_installhandler(libxl_ctx *ctx); /* non-reentrant;logs 
> errs */
> +void libxl__sigchld_removehandler(libxl_ctx *ctx); /* non-reentrant */
> +int libxl__fork_selfpipe_active(libxl_ctx *ctx); /* returns read fd or -1 */
> +void libxl__fork_selfpipe_woken(libxl__egc *egc);
> +int libxl__self_pipe_wakeup(int fd); /* returns 0 or -1 setting errno */
> +int libxl__self_pipe_eatall(int fd); /* returns 0 or -1 setting errno */
> +
> 
>  int libxl__atfork_init(libxl_ctx *ctx);
> 
> --
> 1.7.2.5
> 
> 
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@xxxxxxxxxxxxx
> http://lists.xen.org/xen-devel



_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.