[Tarantool-patches] [PATCH v6 2/4] popen: Introduce a backend engine
Konstantin Osipov
kostja.osipov at gmail.com
Fri Dec 20 11:11:59 MSK 2019
* Cyrill Gorcunov <gorcunov at gmail.com> [19/12/17 15:57]:
> +/* Children pids map popen_handle map */
map popen_handle map?
Even if I remove double "map" I don't undesrtand the ocmment.
Is it pid -> popen_handle() map? How is it used? For waitpid?
Please explain in the comment.
> +static struct mh_i32ptr_t *popen_pids_map = NULL;
> +
> +/* All popen handles to be able to cleanup them on exit */
at exit
> +static RLIST_HEAD(popen_head);
> +
> +/* /dev/null to be used inside children if requested */
This is a bit too sketchy. Please explain: how it is requested,
what it is used for, etc. I can of course guess that it's used
as children stdin/stdout, if there is an option to popen(), but
that's it.
> +static int dev_null_fd_ro = -1;
> +static int dev_null_fd_wr = -1;
> +/**
> + * handle_free - free memory allocated for a handle
> + * @handle: popen handle to free
> + *
> + * Just to match handle_new().
> + */
> +static void
> +handle_free(struct popen_handle *handle)
> +{
> + say_debug("popen: handle %p free %p", handle);
> + free(handle);
> +}
I don't understand who owns char *command and why
it's not freed here. Could you please clarify?
> + return coio_write_fd_timeout(handle->fds[idx],
> + buf, count, timeout);
You could store struct coio for each child stream in the handle,
it's pretty cheap. I don't have a strong opinion on this,
your implementation looks tidy (although has some overhead).
> +/**
> + * popen_notify_sigchld - notify popen subsistem about SIGCHLD event
subsystem
> + if (WIFEXITED(wstatus) || WIFSIGNALED(wstatus)) {
> + assert(handle->pid == pid);
> + /*
> + * libev calls for waitpid by self so
> + * we don't have to wait here.
> + */
> + popen_unregister(handle);
> + /*
> + * Since SIGCHLD may come to us not
> + * due to exit/kill reason (consider
> + * a case when someone stopped a child
> + * process) we should continue wathcing
> + * state changes, thus we stop monitoring
> + * dead children only.
> + */
> + say_debug("popen: ev_child_stop %d", handle->pid);
> + ev_child_stop(EV_DEFAULT_ &handle->ev_sigchld);
> + handle->pid = -1;
> + }
I don't understand how the code matches the comment.
You write "we should continue watching state changes" inside
a branch that handles termination. How is this comment relevant to
what you are doing in this branch?
Why do you have to cast handle->ev_sigchld to EV_DEFAULT_?
> +}
> +
> +/**
> + * ev_sigchld_cb - handle SIGCHLD from a child process
> + * @w: a child exited
> + * @revents: unused
> + */
> +static void
> +ev_sigchld_cb(EV_P_ ev_child *w, int revents)
> +{
> + (void)revents;
> + /*
> + * Stop watching this child, libev will
> + * remove it from own hashtable.
> + */
> + ev_child_stop(EV_A_ w);
> +
> + /*
> + * The reason for a separate helper is that
> + * we might need to notify more subsystems
> + * in future.
> + */
> + popen_notify_sigchld(w->rpid, w->rstatus);
I think ev_sigchld_cb is a too general name. Besides, why do you
need two functions, and call one from another?
> + if (handle->flags & POPEN_FLAGS_SETSID) {
> + if (signo == SIGKILL || signo == SIGTERM) {
> + say_debug("popen: killpg %d signo %d",
> + handle->pid, signo);
> + ret = killpg(handle->pid, signo);
I don't understand why you need to use killpg() here.
Could you please explain?
I suspect that you don't, you're just trying to be safe,
and there is no need to.
> + if (handle->fds[i] != -1) {
> + /*
> + * We might did some i/o on
> + * this fd, so make sure
> + * libev removed it from
> + * watching before close.
> + *
> + * Calling coio_close on
> + * fd which never been watched
> + * if safe.
> + */
> + coio_close(handle->fds[i]);
Ugh. This definitely suggests you need a coio object
in popen_handle(), this coio_close() looks like a hack.
--
Konstantin Osipov, Moscow, Russia
More information about the Tarantool-patches
mailing list