-
Notifications
You must be signed in to change notification settings - Fork 7
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
Run libuv loop on a background thread #37
Conversation
b52a2b3
to
6ce4271
Compare
Some further notes from when I was tinkering with this:
|
3af8c33
to
c6148ea
Compare
Walk me through why running the garbage collector is important for A's event are being running through the garbage collector even if there is no traffic? Is there a memory leak issue somewhere, and this is a temporary fix for that? I don't know what you are trying to solve for. |
weblink/_internal/Server.hx
Outdated
if (data == null) { // EOF | ||
request = null; | ||
stream.close(); | ||
client.close(); | ||
Gc.enable(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't comprehend why after an EOF disabling the GC is a good idea, it will simply be reenabled when another client sends data to the server.
Let's forget for a moment that Weblink exists. We create a TCP listener using HL's libuv bindings: import hl.uv.Loop;
import hl.uv.Tcp;
import sys.net.Host;
import sys.thread.Thread;
function main() {
Thread.create(() -> {
final loop = @:privateAccess Loop.default_loop();
final socket = new Tcp(loop);
socket.bind(new Host("0.0.0.0"), 2000);
socket.listen(100, () -> {});
loop.run(Default);
});
Sys.sleep(1.0);
Sys.println("Hello!");
} As you might expect, this application runs for a second, prints "Hello" and exits. Pretty self-explanatory. Now, before we quit, let's force the garbage collector to run: Sys.sleep(1.0);
+ hl.Gc.major();
Sys.println("Hello!"); The application no longer exits! It also does not print to the console. Moreover, you may also realize that the main thread uses your entire CPU core: You may want to attach a debugger to the main thread. Ideally, if you have a build of HL with some extra info, you might see:
The garbage collector, triggered on the main thread, is waiting for some other thread to be stopped. It's a spinwait, so it should really happen any moment now! But where is that other thread, actually?
It's somewhere in native code, waiting for IO. Turns out stopping a thread that does not want to be stopped is a pretty difficult problem! Let's see if providing said IO will help. curl http://localhost:2000
It did not! The curl request hangs, just like our server. But, you might say, we technically did exit the native code for a moment! Close, but not exactly. The way Hashlink notifies the GC it is allowed to stop a thread is by calling a An example of an operation that would do that is printing to standard output. Or trying to allocate a new object. You can try it out by adding a simple call like this (unless you enable static analyzer and its - socket.listen(100, () -> {});
+ socket.listen(100, () -> {
+ final _:Dynamic = {};
+ }); The application still initially hangs, but unblocks when you try to make a request: curl http://localhost:2000
curl: (56) Recv failure: Connection reset by peer Let's go back to Weblink. As you've seen in my first implementation of this PR, I've decided to disable the GC between TCP events and enable it back as soon as such an event gets raised. I agree it was not an optimal solution and I've since changed it to call As an example, take this comment about saving the socket handlers for later:
|
c6148ea
to
b206d8d
Compare
Great writeup thanks for the detailed response, I see now given how threads behave, gc manipulation is necessary. |
This implementation creates two threads:
haxe.Timer
.Closes #28. Supersedes #36.