Skip to content
This repository

Add control flow functions to uv_listen (pause). #582

Closed
txdv opened this Issue October 08, 2012 · 4 comments

2 participants

Andrius Bentkus Ben Noordhuis
Andrius Bentkus

Hello, I would like to suggest to add uv_listen_pause/uv_listen_resume to the api.
The current api doesn't allow much freedom, you have to listen and if the callback gets hit either you accept or you have lost your cards (the request gets into idle, and you can't reuse use the listener again).

The immediate downside of this that I can see is that someone can bombard the listener with requests and the libuv user can't do much except accepting.

Now bnoordhuis told me to show a usecase.
I am writing currently a wrapper for C# and the newest language feature of C# is await/async.
C# has build in promises, they are called Tasks. They can be executed on different threads but I do excecute them only on the thread where the loop is(because of libuv there is no need for threads).

What the newest feature does is it rewrites synchronous looking code into promises.
And I am currently trying to write a synchronous version of AcceptSocket.
If you call AcceptSocket it blocks until the next incomming connection.

This is how my implementation for now looks like.

public static Task<IUVStream> AcceptStreamAsync(this IListener listener, int backlog)
{
    var tcs = new TaskCompletionSource<IUVStream>();
    try {
        listener.Listen(backlog, (stream) => {
            tcs.SetResult(stream);
        });
    } catch (Exception e) {
        tcs.SetException(e);
    }
    return tcs.Task;
}

When using await in C# you can write code like this:

var socket = await listener.AcceptStereamAsync();

And it will rewrite it under the cover into promises.

The problem is that after calling this function the callback will still get hit(can't pause the listener), even though I am not expressing in my code that I want to get a new connection for now (I want to simulate the blocking AcceptSocket functionality). The solution would be to introduce a pause method:

public static Task<IUVStream> AcceptStreamAsync(this IListener listener, int backlog)
{
    var tcs = new TaskCompletionSource<IUVStream>();
    try {
        listener.Listen(backlog, (stream) => {
            // we pause after getting exactly one socket event
            // and resume when we invoke AcceptStreamAsync again
            listener.Pause();
            tcs.SetResult(stream);
        });
    } catch (Exception e) {
        tcs.SetException(e);
    }
    return tcs.Task;
}

In order to sum up why we need pause: With the pause method libuv would give us more control over the control flow of our programs, it would be as powerful as the blocking version of accept socket, since we could emulate it's behaviour.

Ben Noordhuis

uv_listen_stop() is straightforward to implement in uv-unix, it just needs to stop the listen socket's read watcher.

An edge case is when there is a live connection in stream->accepted_fd but that only happens when the libuv user didn't call uv_accept() in its uv_connection_cb callback. Not really something to worry about, I think.

tl;dr It's easy for me. Things may be more involved for @piscisaureus.

Andrius Bentkus

/cc @piscisaureus we need you!

Andrius Bentkus

One important addition: the sockets can be accepted at a later point, you don't need to accept them in the callback.

Ben Noordhuis

@piscisaureus Call for comments.

Andrius Bentkus txdv closed this October 27, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.