Skip to content
This repository has been archived by the owner on May 4, 2018. It is now read-only.

windows: Stdio handles are still closed even though unix doesn't #991

Open
alexcrichton opened this issue Nov 10, 2013 · 2 comments
Open
Labels

Comments

@alexcrichton
Copy link

It appears that in 359d667 it was changed such that libuv doesn't ever close the stdio file descriptors on unix. The behavior on windows, however, was unchanged, as the file descriptors are still closed. An example program would be:

#include <stdio.h>
#include <uv.h>

uv_loop_t *loop;
uv_tty_t tty;
uv_timer_t timer;

void timercb(uv_timer_t *t, int status) {
    static int hits = 0;
    if (hits++ == 0) {
        uv_close((uv_handle_t*) &tty, NULL);
        uv_timer_start(&timer, timercb, 1, 0);
    } else {
        printf("here\n");
    }
}

int main() {
    loop = uv_default_loop();
    uv_tty_init(loop, &tty, 1, 0);
    uv_timer_init(loop, &timer);

    uv_timer_start(&timer, timercb, 1, 0);

    uv_run(loop, UV_RUN_DEFAULT);
}

On unix, this program currently prints here, but on windows it prints nothing (because uv_close closes stdout).

I'm curious if this is intentional behavior, or whether it's unintended?

@bnoordhuis
Copy link
Contributor

I say that's a uv-win bug. Nothing good comes from closing stdio.

/cc @piscisaureus - agree/disagree?

@piscisaureus
Copy link

I would argue that, on windows, no libuv handles that are created from pre-existing FDs should ever be closed the way we currently do.

The reason is that windows (or rather, msvcrt) implements the file descriptor table in user land. If libuv closes the windows handle the crt doesn't know so it effectively leaves a file descriptor 'dangling' in a broken state. Or, windows might reuse the handle and now suddenly the crt is writing to the wrong thing.

This could of course be fixed by remembering the original file descriptor and closing it with _close as opposed to closing the HANDLE with CloseHandle.

What I dislike about doing 'exceptional' stuff with stdio file descriptors is that it increases the mental strain on the libuv user. Now the poor guy (m/f) needs to remember the exception and program around it accordingly.

A similar effect is visible in the node space. The exceptional rules about (sometimes) blocking stdio is a great source of confusion and obscure bugs. (@isaacs, hello!)

Would it be a better idea to just let the user specify a flag (e.g. UV_NO_CLOSE) to uv_xyz_open so he is aware we'll never actually close the bloody thing? We could also adopt a more generic rule, something akin to "libuv never closes FDs that it didn't open".

On a side note, I have the same beef with our current distinction between tcp, pipe and tty and the need to call uv_guess_handle. There is a small memory usage benefit to it, but the additional effort libuv users have to make in order to support all these handle types (vs a generic stream type) is not worth it imo. Besides it makes libuv needlessly complex because on unix all these are more or less the same anyway (wrt reading and writing it's just another fd) whereas on windows we have to do a lot of special casing on the inside to deal with various open modes for pipes and tcp handles.

I think in hindsight a functional distinction between a 'stream' (tcp, pipe, tty) and 'server' (tcp, pipe) class would have made much more sense than a protocol/implementation based separation.

tl;dr I am happy with any effort to make libuv abstractions better. But try to steer clear of adding implied behavior to avoid (suspected) potential pitfalls. Instead define sensible generic rules and/or add configuration options.

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

No branches or pull requests

3 participants