-
Notifications
You must be signed in to change notification settings - Fork 30
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
libuv is a problem #134
Comments
IMHO I think wren-cli is fine as it is with libuv because it is a project that acts as an example Wren embed. A new wren-cli like project made from scratch or leveraging rust, zig or golang libs can be made as a way to escape libuv problems. But that would be a different project because it would not be considered an "example" project per se, more like a full featured cli tailored for production use. But that are just my two cents. |
I'm not sure what this means, could you please clarity?
Could you link us to your code or a more specific example of the issue? I know I've output ANSI colors easily enough myself, but I'm also not on Windows, so perhaps things are indeed badly broken over there? Also
I'm certainly interested in a faster pace for wren-console (opinionated fork of I just don't think that many are using Wren on the console. |
The major issue is that
Things are not badly broken, the thing is libuv is there to abstract the diff --git a/src/cli/vm.c b/src/cli/vm.c
index 48d2b1e..559b57a 100644
--- a/src/cli/vm.c
+++ b/src/cli/vm.c
@@ -284,6 +284,7 @@ static void initVM()
// Initialize the event loop.
loop = (uv_loop_t*)malloc(sizeof(uv_loop_t));
uv_loop_init(loop);
+ ioInit();
}
static void freeVM()
diff --git a/src/module/io.c b/src/module/io.c
index ad025a1..191eec2 100644
--- a/src/module/io.c
+++ b/src/module/io.c
@@ -18,6 +18,7 @@ typedef struct sFileRequestData
} FileRequestData;
static const int stdinDescriptor = 0;
+static const int stdoutDescriptor = 1;
// Handle to the Stat class object.
static WrenHandle* statClass = NULL;
@@ -31,17 +32,70 @@ static WrenHandle* stdinOnData = NULL;
// The stream used to read from stdin. Initialized on the first read.
static uv_stream_t* stdinStream = NULL;
+// The stream used to read from stdin.
+static uv_stream_t* stdoutStream = NULL;
+
// True if stdin has been set to raw mode.
static bool isStdinRaw = false;
+// Sets up a stream.
+static uv_stream_t* initStream(uv_file fd)
+{
+ if (uv_guess_handle(fd) == UV_TTY)
+ {
+ // stdin is connected to a terminal.
+ uv_tty_t* handle = (uv_tty_t*)malloc(sizeof(uv_tty_t));
+ uv_tty_init(getLoop(), handle, fd, true);
+
+ return (uv_stream_t*)handle;
+ }
+ else
+ {
+ // stdin is a pipe or a file.
+ uv_pipe_t* handle = (uv_pipe_t*)malloc(sizeof(uv_pipe_t));
+ uv_pipe_init(getLoop(), handle, false);
+ uv_pipe_open(handle, fd);
+
+ return (uv_stream_t*)handle;
+ }
+}
+
+#include <io.h>
+
+void ioInit()
+{
+#if 1
+ HANDLE handle;
+ DWORD mode;
+
+ handle = (HANDLE)_get_osfhandle(stdinDescriptor);
+ GetConsoleMode(handle, &mode);
+ SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_INPUT);
+
+ handle = (HANDLE)_get_osfhandle(stdoutDescriptor);
+ GetConsoleMode(handle, &mode);
+ SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
+#endif
+
+ if (stdinStream == NULL) stdinStream = initStream(stdinDescriptor);
+ if (stdoutStream == NULL) stdoutStream = initStream(stdoutDescriptor);
+}
+
+static void finiStream(uv_stream_t* stream)
+{
+ if (stream == NULL) return;
+
+ uv_close((uv_handle_t*)stream, NULL);
+ free(stream);
+}
+
// Frees all resources related to stdin.
static void shutdownStdin()
{
if (stdinStream != NULL)
{
uv_tty_reset_mode();
- uv_close((uv_handle_t*)stdinStream, NULL);
- free(stdinStream);
+ finiStream(stdinStream);
stdinStream = NULL;
}
@@ -58,9 +112,20 @@ static void shutdownStdin()
}
}
+// Frees all resources related to stdin.
+static void shutdownStdout()
+{
+ if (stdoutStream != NULL)
+ {
+ finiStream(stdoutStream);
+ stdoutStream = NULL;
+ }
+}
+
void ioShutdown()
{
shutdownStdin();
+ shutdownStdout();
if (statClass != NULL)
{
@@ -499,30 +564,6 @@ void statIsFile(WrenVM* vm)
wrenSetSlotBool(vm, 0, S_ISREG(stat->st_mode));
}
-// Sets up the stdin stream if not already initialized.
-static void initStdin()
-{
- if (stdinStream == NULL)
- {
- if (uv_guess_handle(stdinDescriptor) == UV_TTY)
- {
- // stdin is connected to a terminal.
- uv_tty_t* handle = (uv_tty_t*)malloc(sizeof(uv_tty_t));
- uv_tty_init(getLoop(), handle, stdinDescriptor, true);
-
- stdinStream = (uv_stream_t*)handle;
- }
- else
- {
- // stdin is a pipe or a file.
- uv_pipe_t* handle = (uv_pipe_t*)malloc(sizeof(uv_pipe_t));
- uv_pipe_init(getLoop(), handle, false);
- uv_pipe_open(handle, stdinDescriptor);
- stdinStream = (uv_stream_t*)handle;
- }
- }
-}
-
void stdinIsRaw(WrenVM* vm)
{
wrenSetSlotBool(vm, 0, isStdinRaw);
@@ -530,8 +571,6 @@ void stdinIsRaw(WrenVM* vm)
void stdinIsRawSet(WrenVM* vm)
{
- initStdin();
-
isStdinRaw = wrenGetSlotBool(vm, 1);
if (uv_guess_handle(stdinDescriptor) == UV_TTY)
@@ -548,7 +587,6 @@ void stdinIsRawSet(WrenVM* vm)
void stdinIsTerminal(WrenVM* vm)
{
- initStdin();
wrenSetSlotBool(vm, 0, uv_guess_handle(stdinDescriptor) == UV_TTY);
}
@@ -611,7 +649,6 @@ static void stdinReadCallback(uv_stream_t* stream, ssize_t numRead,
void stdinReadStart(WrenVM* vm)
{
- initStdin();
uv_read_start(stdinStream, allocCallback, stdinReadCallback);
// TODO: Check return.
}
diff --git a/src/module/io.h b/src/module/io.h
index 7d84786..a668b61 100644
--- a/src/module/io.h
+++ b/src/module/io.h
@@ -3,6 +3,8 @@
#include "wren.h"
+void ioInit();
+
// Frees up any pending resources in use by the IO module.
//
// In particular, this closes down the stdin stream.
Problem is that libuv works in isolation, so you can't subclass a
Sure, the thing is that it is a snow ball effect positively or negatively. If we put some effort on it, it can grow in usage. In the branch I hit the windows issue, I started uniting |
Isn't there just a single loop and single WrenVM? |
Create handles from Wren? That sounds like pretty tight coupling to me... I'd hope the Wren-side API wouldn't be so closely tied to whatever the backend C implementation is... I do agree that wrapping C objects has always seemed overly-hard to me because of the lack of fields... that alone would be a huge improvement if that ever changes. |
I'm talking about making a proper implementation, with possibly multiple loops, and user created |
Like-wise, more proper module binding creation, so it could be somehow more reusable that what is available currently. In any cases, even with changes on wren side to improve the situation, it is still not really clear if a proper binding would be completely possible, because of lifetime control of some libuv objects. |
To what end? At first glance that sounds... unnecessary? One loop (plus a stronger scheduler on the Wren side) should be more than enough, or is using lots of loops a common UV pattern I'm not aware of with some big benefits? On the C side you have to call the loops one after another in any case, unless you're running them in different processes. I think you're talking too theoretical for me... can you give a specific example or two that you'd like to accomplish? Are you talking about wrapping the low-level |
There are multiple reasons to have multiple loops, like handling user inputs differently from general IO to preserve frame-rate on a game/programe or having multiple threaded code acting on different loops (separation of responsibility) or what ever a user strategy to handle event can be. Ideally it would be nice to have only one, but there are situation where having only one is not a valid strategy.
In the contrary, the idea would be to have a more proper |
Sure, I did acknowledge "if you're running them in different processes" (you'd have to define what you mean by thread)... multiple loops might actually be a necessity then... but otherwise you're just running single-threaded C code with a nice event system and callbacks - there is no magic or preemptive multi-tasking happening just because you have two loops.. In my opinion the CLI needs a lot of other things (better base libraries) before it needs full multi-threading/multi-process support - but I'm glad you're not thinking small. :-)
Right now we're tightly coupled at the C layer... it seems you'd just like a nicer wrapper so we could be tightly coupled to UV at the Wren layer... but to me that's still tightly coupled. ...although I agree it might be simpler to manage if the binding was higher-level, with auto-generated bindings, etc... Ultimately though you still have the underlying C/Wren interface though, so I'm not 100% sure what you gain by adding another layer in-between - unless that layer was indeed all auto-generated.
I'm certainly in the more-Wren-is-better camp... |
I wonder if you couldn't handle this pattern with a code generator (for now)... the pattern I've always used was to have two classes, one pure Wren to hold the fields and the other that is all |
Hi,
In an attempt to unite
wrenalyzer
andwrencli
(with the aim to modernize them) I toyed a lot more with this repo, and came to the conclusion thatlibuv
is a problem.There are good parts with it, but there are some issues that will be hard/impossible to circumvent. Due to the API decision to froze their API for eternity (till they change their mind):
master
branch a while ago, but because they reinforced their opinion, libuv version 2 will never probably happen. So the binding options is not possible in a near future (in a sane manner).colors
on windows terminal without introducing platform code in wren (which somehow defeat the point of usinglibuv
). Even without that, even in their latest version, there is no option to triggerENABLE_VIRTUAL_TERMINAL_INPUT
which would solve a bunch of problem like [BUG] "Unhandled key-code [dec]: 8" on Backspace #131 and unify input handling inwrencli
.Changing
ObjForeign
so it so that it could havefields
would helps to solve the first to some extends, but it would not solve the second point.The biggest annoyance is that there is no real alternative solution AFAIK. We can fork
libuv
and fix the issues I noted, but we will end up to have to maintain it, and considering the slow pace of thewren
project it doesn't seems to be viable. We can abandon it, to have our own event library, which does not seems more realistic either considering the platform differences...Any idea/comment is welcome.
The text was updated successfully, but these errors were encountered: