Am 28.01.2013 22:46, schrieb Brian Anderson:
On 01/26/2013 04:07 AM, Michael Neumann wrote:
Am 26.01.2013 13:01, schrieb Michael Neumann:
Am 26.01.2013 12:28, schrieb Michael Neumann:
Another question: When a task sends a message to another task, and
this task is waiting exactly for this event, will it directly
switch to that task, or will it buffer the message?
Sometimes this could be quite handy and efficient. I rember this
was done in the L4 microkernel (www.l4ka.org), which only allowed
synchronous IPC. It could make sense to provide a
send_and_receive directive, which sends to the channel and lets the
scheduler know that it is now waiting for a message to receive from
another port. So send_and_receive could
directly switch to the other task, and when this does a send back
to the calling task, it will switch back to it. If you don't have
send_and_receive as atomic operation, there
is no way to switch back to the other task, as it might still be
running.
"as it might still be running" is here of course wrong (as we
switched to another thread). What I wanted to say is, that it is not
waiting for any event, so it is not in a blocking state, so that
we cannot directly switch back (matching the recv() and the send()).
Ideally the task that wants to read would do the non-blocking I/O
itself, and the scheduler would just notify when it can "read". But
I think this is not possible with libuv as you
have no control over when to read (except using uv_read_start() /
_stop). I think this would be much more efficient and even more
powerful (one can read directly into a buffer...
there is no need to allocate a new buffer for each read as done by
libuv). So what I would suggest is the following:
// task
blocking_read(socket, buffer, ...)
// this will register socket with the schedulers event queue (if
not yet done) and block.
// once the scheduler will receive an "data is available" event
from the kernel
// it will unblock the task.
// then the task will do an non-blocking read() on it's own.
I'm not that familiar with the uv API. Is there a distinct 'data
available' event that happens before we start reading? I've been
assuming that, as you say, we have to control over when the read
events happen, so we would need to check whether the task initiating
this read was currently waiting for data, and either buffer it or
context switch to the task depending on its state.
No there isn't! The reason why, as far as I understand it, lies in the
way Windows handles reads. In UNIX you get notified, when you can read,
while in Windows,
you get notified when a read completed, so you are basically doing the
read asynchronously in the background (saving you another context switch
to the kernel).
I think this is called Proactor (the UNIX-way is called Reactor). libuv
wants to do this in a platform-independent way, where the programmer who
uses libuv does
not have to care about which platform he is working with.
So when we think about this sequence in libuv
uv_read_start(fd)
-> on_read_cb gets triggered
uv_read_stop(fd)
what it does internally is the following:
UNIX:
register event for `fd` in event queue
epoll()
-> allocate buffer
-> read(fd, "nonblocking")
-> call on_read_cb
unregister event
Windows:
allocate buffer
start asynchronous read request
wait for completion (of any outstanding I/O)
-> call on_read_cb
I think libuv is doing too much here. For example, if I don't want to
remove the socket from the event
queue, just disable the callback, then this is not possible. I'd prefer
when I could just tell libuv that
I am interested in event X (on Windows: I/O completion, on UNIX: I/O
availability).
I think a simple hack would be to store the buffer address and size of
buffer in the uv_handle_t structure:
struct our_handle {
uv_handle_t handle;
void *buffer;
size_t buffer_size;
}
and then have the alloc_cb return that:
static uv_buf_t alloc_cb(uv_handle_t *handle, size_t suggested_size)
{
struct our_handle *h = (struct our_handle*)handle;
return uv_buf_init(h->buffer, h->buffer_size);
}
You specify the alloc_cb in uv_read_start(). The only thing that you
need to consider
is that when on_read_cb gets called, you better call uv_read_stop(),
otherwise
the buffer could be overwritten the next time.
Well, yes, this should work for both UNIX and Windows. If you need
specific help, let me know.
I've been hacking a lot with libuv lately and I can't wait using async
I/O in rust (which actually
performs well).
Regards,
Michael
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev