Skip to content

Conversation

tarruda
Copy link
Member

@tarruda tarruda commented Oct 31, 2014

Should close #473

@tarruda tarruda force-pushed the shell-job-refactor branch 3 times, most recently from 02e0370 to ac599de Compare October 31, 2014 10:52
@aktau
Copy link
Contributor

aktau commented Oct 31, 2014

Would it be feasible to do a check at nvim exit time to see if all handles have been properly cleaned up (in debug mode at least)?

@tarruda tarruda force-pushed the shell-job-refactor branch from ac599de to 02c5d8b Compare October 31, 2014 12:14
@tarruda
Copy link
Member Author

tarruda commented Oct 31, 2014

Would it be feasible to do a check at nvim exit time to see if all handles have been properly cleaned up (in debug mode at least)?

I think the proper way to do this is to call uv_run(uv_default_loo(), UV_RUN_DEFAULT) at exit, it will return immediately if no watchers are set.

But if you are talking about the first commit in this PR, the problem is much bigger: Due to the way we interact with libuv, leaving unclosed, stack-allocated handles can have really bad effects, such as a smashed stack. I detected the error caused by those unclosed handles whend debugging #1328

@tarruda tarruda force-pushed the shell-job-refactor branch 7 times, most recently from 63efb1c to 4274467 Compare October 31, 2014 16:26
@splinterofchaos
Copy link

Does this close #473 or do you still want it to allow custom callback? I ask because I've found nice performance increases by doing so (#1263/#1241).

@tarruda
Copy link
Member Author

tarruda commented Oct 31, 2014

Does this close #473 or do you still want it to allow custom callback? I ask because I've found nice performance increases by doing so (#1263/#1241).

I had forgot about those details! Do you mind rebasing your work on top of this PR?

@tarruda
Copy link
Member Author

tarruda commented Oct 31, 2014

Does this close #473 or do you still want it to allow custom callback? I ask because I've found nice performance increases by doing so (#1263/#1241).

os_call_shell is now pretty close to os_system, the biggest difference is the logic to read/write from/to the current buffer. Could the performance improvement you noticed be the consequence of avoiding temporary file redirection when constructing the shell command?

@splinterofchaos
Copy link

I'd be happy to .:)

A little off topic, but #1241 is blocked on not being able to dup stderr and stdout to a single stream. Do you happen to know a way? (Not that I didn't already ask on the mailing list: https://groups.google.com/forum/#!topic/libuv/Q3espD5WWw8)

@tarruda
Copy link
Member Author

tarruda commented Oct 31, 2014

A little off topic, but #1241 is blocked on not being able to dup stderr and stdout to a single stream

Wouldn't that be the same as reading from both streams and appending to a single buffer? That is what os_system does. Maybe you can get a similar performance in #1241 by using os_system to implement make, grep, etc(instead of patching os_call_shell with callbacks). If required, feel free to change os_system to accept extra shell arg, etc

@splinterofchaos
Copy link

Could the performance improvement you noticed be the consequence of avoiding temporary file redirection when constructing the shell command?

I tested :grep and :make with a couple different strategies and here's a list from slowest to fastest (unfortunately, I don't have the raw data anymore):

  1. The current method.
  2. os_system()
  3. job_start()
  4. The current method, but with mkfifo().

Since (4) isn't portable, I figured (3) would be best, but os_system() is indeed an improvement. The advantage to (3) and (4) is that programs like make and ag often have noticeable pauses in their output, so by the time they finish, we can have half the data already processed.

Wouldn't that be the same as reading from both streams and appending to a single buffer?

When I tested :grep with a job, I found the errors ("* is a dir") all bunched together even though stdout and err ran through the same function. Though, I just tested :call system('make') and it looks alright. I might want to try again.

@tarruda
Copy link
Member Author

tarruda commented Oct 31, 2014

I tested :grep and :make with a couple different strategies and here's a list from slowest to fastest (unfortunately, I don't have the raw data anymore):

  1. The current method.
  2. os_system()
  3. job_start()
  4. The current method, but with mkfifo().

Since (4) isn't portable, I figured (3) would be best, but os_system() is indeed an improvement. The advantage to (3) and (4) is that programs like make and ag often have noticeable pauses in their output, so by the time they finish, we can have half the data already processed.

If that's the case, maybe we should simply leave os_call_shell as it is and use job_start directly? It has the advantage of skipping an additional process spawn(the shell)

@tarruda tarruda force-pushed the shell-job-refactor branch from 4274467 to efb8e7c Compare October 31, 2014 17:40
@tarruda tarruda changed the title [WIP] Refactor os_call_shell to use os_system [RDY] Refactor os_call_shell to use os_system Oct 31, 2014
@splinterofchaos
Copy link

maybe we should simply leave os_call_shell as it is and use job_start directly?

Things like :grep dir/* won't work. Though, as an optimization, we could check mch_has_wildcard() first.

@splinterofchaos
Copy link

On second thought, it would be pretty trivial to just call job_start() with sh -c "grep ...".

Commit @709685b4612f4 removed the close_job_* calls when uv_spawn fails because
of memory errors when trying to cleanup unitialized {R,W}Stream instances, but
the uv_pipe_t instances must be closed because they are added to the event loop
queue by previous `uv_pipe_init()` calls
Passing NULL as the callback for stdout/stderr will result in job_start ignoring
stdout/stderr, respectively. A 'writable' boolean argument was also added, and
when false `job_start` will ignore stdin.

Also, refactor os_system to allow passing NULL as the `output` argument.
To follow our coding conventions
Use a timer to periodically compare the current HR time against the HR time of
when `job_stop` was called. After 1 second, send SIGTERM, after 2 seconds, send
SIGKILL. The timer is only active when there's at least one `job_stop` call
pending.
@tarruda tarruda force-pushed the shell-job-refactor branch from efb8e7c to d878569 Compare November 1, 2014 01:52
@tarruda tarruda merged commit d878569 into neovim:master Nov 1, 2014
@aktau
Copy link
Contributor

aktau commented Nov 3, 2014

But of a late review, but apart from my one minor niggle noted in-place, this seems legit (and a good simplification).

// close the input stream, let the process know that no more input is coming
job_close_in(job);
int status = job_wait(job, -1);
status = job_wait(job, -1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can return early from job_wait() (hitting CTRL-C) and there can still be waiting callbacks to system_data_cb().
If shell() returns, the stack allocated buffer buf becomes invalid, but the job callbacks can still try to use buf.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

More improvements for os_call_shell
5 participants