-
Notifications
You must be signed in to change notification settings - Fork 246
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: gain granularity under UNIX by refactoring of the module #73
Comments
unix/pty.cc is the file I've touched the least since forking. So basically you want to expose more stuff in the native API and do more of the handling in JS? |
Yep. I stumbled over the somewhat monolithic c part and had debugging problems in #72 due to it. Separating the basic functionality aspects and exporting them individually to JS would make hunting down those bugs easier. |
@Tyriar Want to bring back this proposal since the current implementation seems to cause several issues (see #134 #85 and #86). For #86 I had a closer look at libuv's To get those isssues above solved a bigger rewrite of the module is needed imho. I hesitate to do such a big rewrite, therefore I want to know want you think about it first. |
@jerch I think this is a good direction to go, just to clarify, here are the goals:
If this is accurate I propose we tackle this in chunks:
|
Agreed. I gonna try to build a first C++ API idea to address the cumbersome C functions like |
@Tyriar Made some progress with an early C++/JS API - see https://github.com/jerch/node-newpty It is a very early version with hardcoded |
@Tyriar Regarding issue #85 I end up at the same problem as before - if the pty device is non blocking it dies as soon as the child process exited discarding leftover data in the pty pipe. Starting it blocking solves this but reintroduces the problem described here chjj/pty.js#103 (comment) No clue yet how to deal with it in a convenient way other than here #86 |
@jerch I think we should lay out what we would expose in terms of a TS method signatures to discuss before jumping into impl, eg. function forkpty(options: { blah })
function ptsname(masterFd: number)
// etc. |
@Tyriar Im far from API sanitizing, still trying to comprehend the conceptual DOs and DONTs with libuv streams and the pty file descriptors. Atm the blocking vs non blocking drives me nuts, non blocking seems to be unreliable with node builtins and Im trying to work around this. Edit: Found a non blocking solution with a polling thread for each open pty Edit2: Pushed a cleaned version with the poller thread ( |
@Tyriar Pushed a cleanup version. You can test it with test.js. stdin and stdout are working as expected now. Its a pity that the solution has to reassemble basic libuv features like the polling just to skip the Some calls are still missing (esp. the |
If stderr is trivial with these changes I guess we should go for it and block it off on Windows with an exception or something. |
@Tyriar Hmm as I thought - the stderr thing is completely unreliable and highly depends on the slave process itself - |
Sounds like it's very much non-trivial and we're asking for a lot of complexity by pursuing this 😛 |
Yep, the whole pty subsystem is not intended to have a separate stderr channel, gonna stop hacking against the OS again ;) - So no extra stderr channel for now... |
Done with the basic C++ transition. Features of the new native lib:
Feel free to play with it (see |
OMG here comes the low level fun - OSX does not support pty file descriptors with |
Update: The poller works now with OSX (tested only on OSX 10.10 though). Edit:
|
@Tyriar Started to move the module to TS and added some first basic tests. Please have a look at it, esp. with TS I need your help (not even sure, if it makes sense what I did at the low level). Imho we can start with the API design. |
Had a look, it's really quite a radical change (literally everything). I'm actually surprised just exposing fork() like that to JS works as well, seems like a risky thing to do as it's not typically done in a native node module? How would you go about launching a new shell with environment/args with this new API? |
fork: new_shell: |
The main issue is I would have no idea what was going on with fork if I didn't learn C in university, I wouldn't expect many JS devs to know what is going on. Perhaps it could be done using 2 callbacks instead? |
I think it is possible to drop the fork & exec stuff and use |
Would something like this work? forkpty(
opts?: I.OpenPtyOptions,
masterCallback: (pid: number, fd: number) => void,
slaveCallback: (pid: number, fd: number) => void
) |
Yes that should work. The master (parent) callback is not needed though. I suggest to "enclose" the child completely in the callback and anything after forkpty(
opts?: I.OpenPtyOptions,
slaveCallback: () => string
)
{
let nativePty: I.NativePty = openpty(opts);
let pid: number = native.fork();
if (pid < 0) {
fs.closeSync(nativePty.master);
fs.closeSync(nativePty.slave);
throw new Error('error running forkpty');
} else if (pid === 0) {
// child
fs.closeSync(nativePty.master);
native.login_tty(nativePty.slave);
let error: string = slaveCallback();
process.stderr.write(error);
process.exit(-1);
}
// parent
fs.closeSync(nativePty.slave);
return {pid: pid, fd: nativePty.master, slavepath: nativePty.slavepath};
} It still exposes the limitations of the child in the callback though, the callback code must be synchron and exec early. A call would look like this: let forked = forkpty(options, () => {return native.execvp('bash', ['bash', '-l']);});
// do something with the symbols in `forked` If you still feel uneasy about this we could even hard code the exec* stuff and just let people use that higher API, where they can only set parameters to the exec* calls. Thats what |
About |
Yes with the helper binary it works under all POSIX systems I can test here (see https://github.com/jerch/node-newpty/tree/child_process). Maybe we should build the unix stuff based on that idea? How hard would it be to move the windows stuff to the ChildProcess-API as well? Edit: Cleaned up that branch - much shorter now, most of the code is obsolete with |
Windows is mostly a black box for me beyond the winpty public API atm https://github.com/rprichard/winpty/blob/master/src/include/winpty.h |
I have implemented a separate STDERR pipe option. Tested with a small helper binary as direct child, grandchild (behind |
Set up a |
Done with the basic implementation. Features now:
Gonna try to plant it under the current API for compatibility. Also Solaris keeps bugging me, need to find a fix for the awkward pty behavior there. Under NetBSD libuv tends to crash, no clue yet why. Works under Linux, OSX, FreeBSD and OpenBSD and passes all tests. |
@Tyriar The basic stuff is done, got a working The IO poller is on par to slightly faster compared to |
Nope does not work with lib_uv, neither with |
@Tyriar Had some ehem fun with node's event loop, tried to get rid of the polling thread with those additional pipes - well, without success. The libuv primitives for IO polling are not capable to handle the special Nonetheless the new implementation even with poll thread and additional pipes runs 2 times faster than the old node-pty implementation and even faster with more concurrent ptys open. Gonna cleanup stuff, then it is ready for a review. BTW found several issues in the current node-pty for unix while testing around. |
@Tyriar Did some cleanup, feel free to play around with the new unix implementation. Maybe you can also test it on a newer OSX version (can only test 10.10 here). A few notes:
|
@jerch probably won't have time to look at this for some time unfortunately, I've been out attending events and next month will be very focused on improving Windows support on the terminal. |
No problem, the source will not run away ;) |
@jerch are there any major take aways you got from your refactor? It would be cool to integrate some of it into the repo, it's just I don't think it would be wise to do a huge PR which changes everything. Instead safer incremental changes to make sure we don't break too much as I like to always ship the latest node-pty in VS Code Insiders, radical changes scare me 😛 |
@Tyriar Hmm well, imho the most annoying bug in the current node-pty impl is the truncated output if you run a process that exits right after. For typical shell usage (as with xterm.js) this is no biggy, the bug hardly will be triggered since the shell just sits there and waits for input giving the underlying pty <--> libuv loop time to process all data. For any non shell based pty usage this is a showstopper. The refactor deals with that problem by the custom poll thread with additional os pipes. Sadly this cant be cherrypicked easily as it changes the internal API to separated STDIN and STOUT. Next big change - For unix users the ability to alter the termios settings from JS might be of interest. This could be applied with small changes to node-pty (see https://github.com/jerch/node-newpty/blob/master/src/pty.ts#L380, which is almost a copy of the current C code in node-pty). |
@jerch so i think we do see the issue out dropped output in vscode as we now use the terminal for short-lived tasks that run one command and then terminate, Right now I workaround this with the following code: Changing the Terminal class is kind of just changing the entire library and might be too radical to pull into this (as no one would bother upgrading). I'm also curious what you thought of #151 |
@Tyriar The right place to fix this bug once and for all would be the libuv poller, it just cant handle the special condition that happens on the master fd ( About the Terminal class - yepp, I agree. Thats why I implemented the UnixTerminal on top of new |
I'm moving all issues related to |
@Tyriar Not sure how to name it - well I think at least the unix part of this module is kinda broken by design. I've done some own investigations by trying out a much simpler pty implementation under linux and came to the following conclusion:
The C part of the module does to much at once, esp. the
Ptyfork
is heavy weighted - it tries to deal withforkpty
, termios settings, theexec*
call and registering of the waitpid callback at once - my prosopal is to separate those things into single calls exported to JS and gain much more interactivity in JS land. Also it would makepty.open
useful again. Atmpty.open
is a dead end since the needed symbols from C are missing.Changes of my proposal:
forkpty
in C semanticsexec*
functions in C semanticsfork
(to be used withopenpty
)wait*
functionsNote that those changes would degrade the module to basic C semantics. Therefore it would need to set up higher level stuff in
UnixTerminal
.The text was updated successfully, but these errors were encountered: