Created
June 13, 2017 09:59
-
-
Save jonthn/5e34b7b516b6297386464de9ffcddf24 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- work/neovim/src/nvim/event/process.c 2017-06-08 09:36:08.000000000 +0200 | |
+++ process.c 2017-06-13 11:51:28.000000000 +0200 | |
@@ -34,9 +34,37 @@ | |
static bool process_is_tearing_down = false; | |
+static void | |
+process_exists(void *prgm) | |
+{ | |
+ int *pid = prgm; | |
+ while (0 < getpgid(*pid)) | |
+ { | |
+ ILOG("PROCESS %d is alive", *pid); | |
+ usleep(50); | |
+ } | |
+ ILOG("PROCESS %d has quit", *pid); | |
+ free(pid); | |
+} | |
+ | |
/// @returns zero on success, or negative error code | |
int process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL | |
{ | |
+ ILOG("type %d", proc->type); | |
+ if (proc->argv) | |
+ { | |
+ int pos = 0; | |
+ char *euh = NULL; | |
+ euh = proc->argv[pos]; | |
+ do | |
+ { | |
+ ILOG("--- %s", euh); | |
+ pos++; | |
+ euh = proc->argv[pos]; | |
+ | |
+ } while (euh != NULL); | |
+ | |
+ } | |
if (proc->in) { | |
uv_pipe_init(&proc->loop->uv, &proc->in->uv.pipe, 0); | |
} | |
@@ -58,9 +86,23 @@ | |
status = pty_process_spawn((PtyProcess *)proc); | |
break; | |
default: | |
+ ILOG("%s:%d", __func__, __LINE__); | |
abort(); | |
} | |
+ ILOG("process %d %s", proc->pid, 0 < getpgid(proc->pid) ? "exists" : "disappeared"); | |
+ | |
+ /** DEBUG process exists */ | |
+ pthread_t thr_ex; | |
+ pthread_attr_t attr; | |
+ | |
+ pthread_attr_init(&attr); | |
+ | |
+ int *pid_in_heap = calloc(1, sizeof *pid_in_heap); | |
+ *pid_in_heap = proc->pid; | |
+ pthread_create(&thr_ex, &attr, process_exists, pid_in_heap); | |
+ | |
+ ILOG("launched pid %d : %d", proc->pid, status); | |
if (status) { | |
if (proc->in) { | |
uv_close((uv_handle_t *)&proc->in->uv.pipe, NULL); | |
@@ -79,9 +121,12 @@ | |
} | |
shell_free_argv(proc->argv); | |
proc->status = -1; | |
+ ILOG("process %d %s", proc->pid, 0 < getpgid(proc->pid) ? "exists" : "disappeared"); | |
+ ILOG("%d status %d", proc->pid, status); | |
return status; | |
} | |
+ ILOG("process %d %s", proc->pid, 0 < getpgid(proc->pid) ? "exists" : "disappeared"); | |
if (proc->in) { | |
stream_init(NULL, proc->in, -1, | |
STRUCT_CAST(uv_stream_t, &proc->in->uv.pipe)); | |
@@ -109,15 +154,23 @@ | |
proc->refcount++; | |
} | |
+ ILOG("process %d %s", proc->pid, 0 < getpgid(proc->pid) ? "exists" : "disappeared"); | |
proc->internal_exit_cb = on_process_exit; | |
proc->internal_close_cb = decref; | |
proc->refcount++; | |
kl_push(WatcherPtr, proc->loop->children, proc); | |
+ ILOG("%d added to children", proc->pid); | |
+ | |
return 0; | |
} | |
void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL | |
{ | |
+ ILOG("%s:%d ====== ", __func__, __LINE__); | |
+ | |
+ kliter_t(WatcherPtr) **nodes[128] = {0}; | |
+ int nodes_num = 0; | |
+ | |
process_is_tearing_down = true; | |
kl_iter(WatcherPtr, loop->children, current) { | |
Process *proc = (*current)->data; | |
@@ -125,40 +178,68 @@ | |
// Close handles to process without killing it. | |
CREATE_EVENT(loop->events, process_close_handles, 1, proc); | |
} else { | |
- uv_kill(proc->pid, SIGTERM); | |
+ ILOG("%s:%d pid: %d", __func__, __LINE__, proc->pid); | |
+ uv_errno_t r = uv_kill(proc->pid, SIGTERM); | |
proc->term_sent = true; | |
- process_stop(proc); | |
+ if (UV_ESRCH == r) | |
+ { | |
+ ILOG("%s:%d pid: %d doesn't exist anymore", __func__, __LINE__, proc->pid); | |
+ if (nodes_num < 128 - 1) | |
+ nodes[nodes_num++] = current; | |
+ } else { | |
+ ILOG("%s:%d pid: %d", __func__, __LINE__, proc->pid); | |
+ process_stop(proc); | |
+ } | |
} | |
} | |
+ ILOG("%s:%d %d", __func__, __LINE__, kl_empty(loop->children)); | |
+ for (int node_i = 0; node_i < nodes_num ; node_i++) | |
+ { | |
+ kl_shift_at(WatcherPtr, loop->children, nodes[node_i]); | |
+ } | |
+ ILOG("%s:%d %d", __func__, __LINE__, kl_empty(loop->children)); | |
+ ILOG("%s:%d %d", __func__, __LINE__, multiqueue_empty(loop->events)); | |
+ | |
// Wait until all children exit and all close events are processed. | |
LOOP_PROCESS_EVENTS_UNTIL( | |
loop, loop->events, -1, | |
kl_empty(loop->children) && multiqueue_empty(loop->events)); | |
pty_process_teardown(loop); | |
+ ILOG("%s:%d", __func__, __LINE__); | |
} | |
// Wrappers around `stream_close` that protect against double-closing. | |
void process_close_streams(Process *proc) FUNC_ATTR_NONNULL_ALL | |
{ | |
+ ILOG("%d 0/3", proc->pid); | |
process_close_in(proc); | |
+ ILOG("%d 1/3", proc->pid); | |
process_close_out(proc); | |
+ ILOG("%d 2/3", proc->pid); | |
process_close_err(proc); | |
+ ILOG("%d 3/3", proc->pid); | |
} | |
void process_close_in(Process *proc) FUNC_ATTR_NONNULL_ALL | |
{ | |
+ ILOG("%d", proc->pid); | |
CLOSE_PROC_STREAM(proc, in); | |
+ ILOG("%d", proc->pid); | |
} | |
void process_close_out(Process *proc) FUNC_ATTR_NONNULL_ALL | |
{ | |
+ ILOG("%d", proc->pid); | |
CLOSE_PROC_STREAM(proc, out); | |
+ ILOG("%d", proc->pid); | |
} | |
void process_close_err(Process *proc) FUNC_ATTR_NONNULL_ALL | |
{ | |
+ ILOG("%d", proc->pid); | |
CLOSE_PROC_STREAM(proc, err); | |
+ ILOG("%d", proc->pid); | |
} | |
/// Synchronously wait for a process to finish | |
@@ -172,11 +253,14 @@ | |
int process_wait(Process *proc, int ms, MultiQueue *events) | |
FUNC_ATTR_NONNULL_ARG(1) | |
{ | |
+ ILOG("%d wait for %d ms", __func__, __LINE__, proc->pid, ms); | |
int status = -1; // default | |
bool interrupted = false; | |
if (!proc->refcount) { | |
status = proc->status; | |
LOOP_PROCESS_EVENTS(proc->loop, proc->events, 0); | |
+ | |
+ ILOG("%d status %d", proc->pid, status); | |
return status; | |
} | |
@@ -221,13 +305,16 @@ | |
proc->refcount--; | |
} | |
+ ILOG("%d status %d", proc->pid, status); | |
return status; | |
} | |
/// Ask a process to terminate and eventually kill if it doesn't respond | |
void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL | |
{ | |
+ ILOG("%d", proc->pid); | |
if (proc->stopped_time) { | |
+ ILOG("%d", proc->pid); | |
return; | |
} | |
@@ -245,6 +332,7 @@ | |
pty_process_close_master((PtyProcess *)proc); | |
break; | |
default: | |
+ ILOG("%d", proc->pid); | |
abort(); | |
} | |
@@ -252,15 +340,17 @@ | |
if (!loop->children_stop_requests++) { | |
// When there's at least one stop request pending, start a timer that | |
// will periodically check if a signal should be send to a to the job | |
- DLOG("Starting job kill timer"); | |
+ ILOG("Starting job kill timer"); | |
uv_timer_start(&loop->children_kill_timer, children_kill_cb, 100, 100); | |
} | |
+ ILOG("%d", proc->pid); | |
} | |
/// Iterates the process list sending SIGTERM to stopped processes and SIGKILL | |
/// to those that didn't die from SIGTERM after a while(exit_timeout is 0). | |
static void children_kill_cb(uv_timer_t *handle) | |
{ | |
+ ILOG("BEGIN"); | |
Loop *loop = handle->loop->data; | |
uint64_t now = os_hrtime(); | |
@@ -280,11 +370,13 @@ | |
uv_kill(proc->pid, SIGKILL); | |
} | |
} | |
+ ILOG("END"); | |
} | |
static void process_close_event(void **argv) | |
{ | |
Process *proc = argv[0]; | |
+ ILOG("%d", proc->pid); | |
shell_free_argv(proc->argv); | |
if (proc->type == kProcessTypePty) { | |
xfree(((PtyProcess *)proc)->term_name); | |
@@ -292,6 +384,7 @@ | |
if (proc->cb) { | |
proc->cb(proc, proc->status, proc->data); | |
} | |
+ ILOG("%d", proc->pid); | |
} | |
static void decref(Process *proc) | |
@@ -316,10 +409,12 @@ | |
static void process_close(Process *proc) | |
FUNC_ATTR_NONNULL_ARG(1) | |
{ | |
+ ILOG("%d", proc->pid); | |
if (process_is_tearing_down && (proc->detach || proc->type == kProcessTypePty) | |
&& proc->closed) { | |
// If a detached/pty process dies while tearing down it might get closed | |
// twice. | |
+ ILOG("%d", proc->pid); | |
return; | |
} | |
assert(!proc->closed); | |
@@ -332,8 +427,10 @@ | |
pty_process_close((PtyProcess *)proc); | |
break; | |
default: | |
+ ILOG("%d", proc->pid); | |
abort(); | |
} | |
+ ILOG("%d", proc->pid); | |
} | |
/// Flush output stream. | |
@@ -343,7 +440,9 @@ | |
static void flush_stream(Process *proc, Stream *stream) | |
FUNC_ATTR_NONNULL_ARG(1) | |
{ | |
+ ILOG("%d", proc->pid); | |
if (!stream || stream->closed) { | |
+ ILOG("%d", proc->pid); | |
return; | |
} | |
@@ -382,40 +481,52 @@ | |
break; | |
} | |
} | |
+ ILOG("%d", proc->pid); | |
} | |
static void process_close_handles(void **argv) | |
{ | |
Process *proc = argv[0]; | |
+ ILOG("%d", proc->pid); | |
+ ILOG("%d 0/4", proc->pid); | |
flush_stream(proc, proc->out); | |
+ ILOG("%d 1/4", proc->pid); | |
flush_stream(proc, proc->err); | |
+ ILOG("%d 2/4", proc->pid); | |
process_close_streams(proc); | |
+ ILOG("%d 3/4", proc->pid); | |
process_close(proc); | |
+ ILOG("%d 4/4", proc->pid); | |
} | |
static void on_process_exit(Process *proc) | |
{ | |
+ ILOG("%d finished !!!", proc->pid); | |
Loop *loop = proc->loop; | |
if (proc->stopped_time && loop->children_stop_requests | |
&& !--loop->children_stop_requests) { | |
// Stop the timer if no more stop requests are pending | |
- DLOG("Stopping process kill timer"); | |
+ ILOG("Stopping process kill timer"); | |
uv_timer_stop(&loop->children_kill_timer); | |
} | |
+ | |
// Process has terminated, but there could still be data to be read from the | |
// OS. We are still in the libuv loop, so we cannot call code that polls for | |
// more data directly. Instead delay the reading after the libuv loop by | |
// queueing process_close_handles() as an event. | |
MultiQueue *queue = proc->events ? proc->events : loop->events; | |
CREATE_EVENT(queue, process_close_handles, 1, proc); | |
+ ILOG("%d", proc->pid); | |
} | |
static void on_process_stream_close(Stream *stream, void *data) | |
{ | |
Process *proc = data; | |
+ ILOG("%d", proc->pid); | |
decref(proc); | |
+ ILOG("%d", proc->pid); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- work/neovim/src/nvim/event/process.c 2017-06-08 09:36:08.000000000 +0200 | |
+++ process.c 2017-06-13 11:51:28.000000000 +0200 | |
@@ -34,9 +34,37 @@ | |
static bool process_is_tearing_down = false; | |
+static void | |
+process_exists(void *prgm) | |
+{ | |
+ int *pid = prgm; | |
+ while (0 < getpgid(*pid)) | |
+ { | |
+ ILOG("PROCESS %d is alive", *pid); | |
+ usleep(50); | |
+ } | |
+ ILOG("PROCESS %d has quit", *pid); | |
+ free(pid); | |
+} | |
+ | |
/// @returns zero on success, or negative error code | |
int process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL | |
{ | |
+ ILOG("type %d", proc->type); | |
+ if (proc->argv) | |
+ { | |
+ int pos = 0; | |
+ char *euh = NULL; | |
+ euh = proc->argv[pos]; | |
+ do | |
+ { | |
+ ILOG("--- %s", euh); | |
+ pos++; | |
+ euh = proc->argv[pos]; | |
+ | |
+ } while (euh != NULL); | |
+ | |
+ } | |
if (proc->in) { | |
uv_pipe_init(&proc->loop->uv, &proc->in->uv.pipe, 0); | |
} | |
@@ -58,9 +86,23 @@ | |
status = pty_process_spawn((PtyProcess *)proc); | |
break; | |
default: | |
+ ILOG("%s:%d", __func__, __LINE__); | |
abort(); | |
} | |
+ ILOG("process %d %s", proc->pid, 0 < getpgid(proc->pid) ? "exists" : "disappeared"); | |
+ | |
+ /** DEBUG process exists */ | |
+ pthread_t thr_ex; | |
+ pthread_attr_t attr; | |
+ | |
+ pthread_attr_init(&attr); | |
+ | |
+ int *pid_in_heap = calloc(1, sizeof *pid_in_heap); | |
+ *pid_in_heap = proc->pid; | |
+ pthread_create(&thr_ex, &attr, process_exists, pid_in_heap); | |
+ | |
+ ILOG("launched pid %d : %d", proc->pid, status); | |
if (status) { | |
if (proc->in) { | |
uv_close((uv_handle_t *)&proc->in->uv.pipe, NULL); | |
@@ -79,9 +121,12 @@ | |
} | |
shell_free_argv(proc->argv); | |
proc->status = -1; | |
+ ILOG("process %d %s", proc->pid, 0 < getpgid(proc->pid) ? "exists" : "disappeared"); | |
+ ILOG("%d status %d", proc->pid, status); | |
return status; | |
} | |
+ ILOG("process %d %s", proc->pid, 0 < getpgid(proc->pid) ? "exists" : "disappeared"); | |
if (proc->in) { | |
stream_init(NULL, proc->in, -1, | |
STRUCT_CAST(uv_stream_t, &proc->in->uv.pipe)); | |
@@ -109,15 +154,23 @@ | |
proc->refcount++; | |
} | |
+ ILOG("process %d %s", proc->pid, 0 < getpgid(proc->pid) ? "exists" : "disappeared"); | |
proc->internal_exit_cb = on_process_exit; | |
proc->internal_close_cb = decref; | |
proc->refcount++; | |
kl_push(WatcherPtr, proc->loop->children, proc); | |
+ ILOG("%d added to children", proc->pid); | |
+ | |
return 0; | |
} | |
void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL | |
{ | |
+ ILOG("%s:%d ====== ", __func__, __LINE__); | |
+ | |
+ kliter_t(WatcherPtr) **nodes[128] = {0}; | |
+ int nodes_num = 0; | |
+ | |
process_is_tearing_down = true; | |
kl_iter(WatcherPtr, loop->children, current) { | |
Process *proc = (*current)->data; | |
@@ -125,40 +178,68 @@ | |
// Close handles to process without killing it. | |
CREATE_EVENT(loop->events, process_close_handles, 1, proc); | |
} else { | |
- uv_kill(proc->pid, SIGTERM); | |
+ ILOG("%s:%d pid: %d", __func__, __LINE__, proc->pid); | |
+ uv_errno_t r = uv_kill(proc->pid, SIGTERM); | |
proc->term_sent = true; | |
- process_stop(proc); | |
+ if (UV_ESRCH == r) | |
+ { | |
+ ILOG("%s:%d pid: %d doesn't exist anymore", __func__, __LINE__, proc->pid); | |
+ if (nodes_num < 128 - 1) | |
+ nodes[nodes_num++] = current; | |
+ } else { | |
+ ILOG("%s:%d pid: %d", __func__, __LINE__, proc->pid); | |
+ process_stop(proc); | |
+ } | |
} | |
} | |
+ ILOG("%s:%d %d", __func__, __LINE__, kl_empty(loop->children)); | |
+ for (int node_i = 0; node_i < nodes_num ; node_i++) | |
+ { | |
+ kl_shift_at(WatcherPtr, loop->children, nodes[node_i]); | |
+ } | |
+ ILOG("%s:%d %d", __func__, __LINE__, kl_empty(loop->children)); | |
+ ILOG("%s:%d %d", __func__, __LINE__, multiqueue_empty(loop->events)); | |
+ | |
// Wait until all children exit and all close events are processed. | |
LOOP_PROCESS_EVENTS_UNTIL( | |
loop, loop->events, -1, | |
kl_empty(loop->children) && multiqueue_empty(loop->events)); | |
pty_process_teardown(loop); | |
+ ILOG("%s:%d", __func__, __LINE__); | |
} | |
// Wrappers around `stream_close` that protect against double-closing. | |
void process_close_streams(Process *proc) FUNC_ATTR_NONNULL_ALL | |
{ | |
+ ILOG("%d 0/3", proc->pid); | |
process_close_in(proc); | |
+ ILOG("%d 1/3", proc->pid); | |
process_close_out(proc); | |
+ ILOG("%d 2/3", proc->pid); | |
process_close_err(proc); | |
+ ILOG("%d 3/3", proc->pid); | |
} | |
void process_close_in(Process *proc) FUNC_ATTR_NONNULL_ALL | |
{ | |
+ ILOG("%d", proc->pid); | |
CLOSE_PROC_STREAM(proc, in); | |
+ ILOG("%d", proc->pid); | |
} | |
void process_close_out(Process *proc) FUNC_ATTR_NONNULL_ALL | |
{ | |
+ ILOG("%d", proc->pid); | |
CLOSE_PROC_STREAM(proc, out); | |
+ ILOG("%d", proc->pid); | |
} | |
void process_close_err(Process *proc) FUNC_ATTR_NONNULL_ALL | |
{ | |
+ ILOG("%d", proc->pid); | |
CLOSE_PROC_STREAM(proc, err); | |
+ ILOG("%d", proc->pid); | |
} | |
/// Synchronously wait for a process to finish | |
@@ -172,11 +253,14 @@ | |
int process_wait(Process *proc, int ms, MultiQueue *events) | |
FUNC_ATTR_NONNULL_ARG(1) | |
{ | |
+ ILOG("%d wait for %d ms", __func__, __LINE__, proc->pid, ms); | |
int status = -1; // default | |
bool interrupted = false; | |
if (!proc->refcount) { | |
status = proc->status; | |
LOOP_PROCESS_EVENTS(proc->loop, proc->events, 0); | |
+ | |
+ ILOG("%d status %d", proc->pid, status); | |
return status; | |
} | |
@@ -221,13 +305,16 @@ | |
proc->refcount--; | |
} | |
+ ILOG("%d status %d", proc->pid, status); | |
return status; | |
} | |
/// Ask a process to terminate and eventually kill if it doesn't respond | |
void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL | |
{ | |
+ ILOG("%d", proc->pid); | |
if (proc->stopped_time) { | |
+ ILOG("%d", proc->pid); | |
return; | |
} | |
@@ -245,6 +332,7 @@ | |
pty_process_close_master((PtyProcess *)proc); | |
break; | |
default: | |
+ ILOG("%d", proc->pid); | |
abort(); | |
} | |
@@ -252,15 +340,17 @@ | |
if (!loop->children_stop_requests++) { | |
// When there's at least one stop request pending, start a timer that | |
// will periodically check if a signal should be send to a to the job | |
- DLOG("Starting job kill timer"); | |
+ ILOG("Starting job kill timer"); | |
uv_timer_start(&loop->children_kill_timer, children_kill_cb, 100, 100); | |
} | |
+ ILOG("%d", proc->pid); | |
} | |
/// Iterates the process list sending SIGTERM to stopped processes and SIGKILL | |
/// to those that didn't die from SIGTERM after a while(exit_timeout is 0). | |
static void children_kill_cb(uv_timer_t *handle) | |
{ | |
+ ILOG("BEGIN"); | |
Loop *loop = handle->loop->data; | |
uint64_t now = os_hrtime(); | |
@@ -280,11 +370,13 @@ | |
uv_kill(proc->pid, SIGKILL); | |
} | |
} | |
+ ILOG("END"); | |
} | |
static void process_close_event(void **argv) | |
{ | |
Process *proc = argv[0]; | |
+ ILOG("%d", proc->pid); | |
shell_free_argv(proc->argv); | |
if (proc->type == kProcessTypePty) { | |
xfree(((PtyProcess *)proc)->term_name); | |
@@ -292,6 +384,7 @@ | |
if (proc->cb) { | |
proc->cb(proc, proc->status, proc->data); | |
} | |
+ ILOG("%d", proc->pid); | |
} | |
static void decref(Process *proc) | |
@@ -316,10 +409,12 @@ | |
static void process_close(Process *proc) | |
FUNC_ATTR_NONNULL_ARG(1) | |
{ | |
+ ILOG("%d", proc->pid); | |
if (process_is_tearing_down && (proc->detach || proc->type == kProcessTypePty) | |
&& proc->closed) { | |
// If a detached/pty process dies while tearing down it might get closed | |
// twice. | |
+ ILOG("%d", proc->pid); | |
return; | |
} | |
assert(!proc->closed); | |
@@ -332,8 +427,10 @@ | |
pty_process_close((PtyProcess *)proc); | |
break; | |
default: | |
+ ILOG("%d", proc->pid); | |
abort(); | |
} | |
+ ILOG("%d", proc->pid); | |
} | |
/// Flush output stream. | |
@@ -343,7 +440,9 @@ | |
static void flush_stream(Process *proc, Stream *stream) | |
FUNC_ATTR_NONNULL_ARG(1) | |
{ | |
+ ILOG("%d", proc->pid); | |
if (!stream || stream->closed) { | |
+ ILOG("%d", proc->pid); | |
return; | |
} | |
@@ -382,40 +481,52 @@ | |
break; | |
} | |
} | |
+ ILOG("%d", proc->pid); | |
} | |
static void process_close_handles(void **argv) | |
{ | |
Process *proc = argv[0]; | |
+ ILOG("%d", proc->pid); | |
+ ILOG("%d 0/4", proc->pid); | |
flush_stream(proc, proc->out); | |
+ ILOG("%d 1/4", proc->pid); | |
flush_stream(proc, proc->err); | |
+ ILOG("%d 2/4", proc->pid); | |
process_close_streams(proc); | |
+ ILOG("%d 3/4", proc->pid); | |
process_close(proc); | |
+ ILOG("%d 4/4", proc->pid); | |
} | |
static void on_process_exit(Process *proc) | |
{ | |
+ ILOG("%d finished !!!", proc->pid); | |
Loop *loop = proc->loop; | |
if (proc->stopped_time && loop->children_stop_requests | |
&& !--loop->children_stop_requests) { | |
// Stop the timer if no more stop requests are pending | |
- DLOG("Stopping process kill timer"); | |
+ ILOG("Stopping process kill timer"); | |
uv_timer_stop(&loop->children_kill_timer); | |
} | |
+ | |
// Process has terminated, but there could still be data to be read from the | |
// OS. We are still in the libuv loop, so we cannot call code that polls for | |
// more data directly. Instead delay the reading after the libuv loop by | |
// queueing process_close_handles() as an event. | |
MultiQueue *queue = proc->events ? proc->events : loop->events; | |
CREATE_EVENT(queue, process_close_handles, 1, proc); | |
+ ILOG("%d", proc->pid); | |
} | |
static void on_process_stream_close(Stream *stream, void *data) | |
{ | |
Process *proc = data; | |
+ ILOG("%d", proc->pid); | |
decref(proc); | |
+ ILOG("%d", proc->pid); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment