Am 25.01.2013 04:01, schrieb Brian Anderson:
On 01/24/2013 04:37 PM, Patrick Walton wrote:
On 1/24/13 3:55 PM, Michael Neumann wrote:
* I don't like the way libuv is currently integrated into the
system. It
I sympathize.
:)
works, but performance is
quite low and IMHO the blocking interface is not very usable. For
example I want to write a process
that accepts messages from other processes, and then writes
something to
the socket or reads from
the socket. This will currently not work, as reading from the socket
will block the process, and
then no more requests can be sent to the process.
So instead of using the read() / write() API of an io::Reader, I'd
prefer to expose the read/write
events of libuv via messages (this is already done between the iotask
and the read()/write() methods,
but it is not accessible to the "end-user").
So instead of:
io.read(...)
one would simply write:
readport.recv()
Both of these are blocking. The significant advantage of using a port
here though is that core::pipes has several ways to receive on
multiple ports at once, so you could wait for both the read event and
some other signal instead of being stuck until the read either
succeeds or times out.
Exactly!
There is a lot of overlap functionally between Port/Chan and
Reader/Writer (std::flatpipes event implements the GenericPort and
GenericChan traits on top of Reader and Writer). They both have their
strengths though. Streaming data over channels is just going to leave
you with a bunch of byte buffers, whereas Readers give you lots of
control over how to interpret those bytes. I could see the api here
being channel based, letting the user opt into Reader and Writer
implementations for Port<~[u8]> etc. as needed.
The same for writes. EOF results in closing the readport. The question
is how these messages
should look like to be usable for the programmer (how to handle
errors?).
What do you think?
Actually there would be connecting ports, which receive events whenever
a new connection is established.
A successfully established connection would then be represented by a
readport and writechannel.
brson is working on a rewrite of the scheduler. This new scheduler
should run directly on the libuv event loop. This should have much
higher performance.
The current implementation will go away completely. It was useful as a
prototype but it has problems. The new intent is to design the Rust
scheduler so that it can be driven by an arbitrary event loop, then
use the uv event loop for that purpose by default (this could also be
useful for e.g. integrating with the Win32 event loop, etc.). The main
advantage of integrating uv into the scheduler over the current design
is that there will be much less synchronization and context switching
to dispatch events. This will unfortunately necessitate a complex
integration of the scheduler and the I/O system and I don't know how
that is going to work yet.
Do you mean that the libuv callbacks -> messages conversion will then go
away? Currenctly every callback is sent from the iotask to the
"interested" task via a message.
Will the new design be different in this regard? I'd like to hear more
details about what you are trying to accomplish. Is there already some code?
I think another thing that currently makes I/O slow is that for every
read() call, at first a messages is sent to the iotask to let the libuv
know that we want to start reading from
the socket (uv_read_start()). Then we actually request the read (another
message) and finally we stop reading again (uv_read_stop()). So in
total, every read will involve 3 complete
message cycles between iotask and the requesting task. I hope this will
be going to be reduced to just one (but this is probably just a library
issue and not related to the scheduler...).
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.
In L4 it is always the sender who blocks when the receiving side is not
ready. How about the pipes implementation in Rust?
What I noticed when doing some benchmarks with I/O was that the timing
results did vary a lot. I think this is due to the scheduling.
* I'd like to know more how the task scheduler and the pipes work
together. Is there any info available somewhere?
I think brson knows best.
There's no info besides the source code. The relevant files are
`src/libcore/pipes.rs` and `src/rt/rust_task.cpp`. Pipes uses three
foreign calls to indirectly control the scheduler: `task_wait_event`,
`task_signal_event`, `task_clear_event_reject`, the details of which I
don't know off hand but which look fairly straightforward. The
implementation is made more complex by the continued existence of
`core::oldcomm`, which uses a slightly different method of signalling
events and relies on much more foreign code.
What's the current branch you are working on the new scheduler? Is it
newsched of brson/rust?
Also, if I would create a native pthread in C, could I simply call an
external rust function?
Not yet. Today all Rust code depends on the Rust runtime (I've been
saying that code must be run 'in task context'). Creating a pthread
puts you outside of a task context. Being in task context essentially
means that various runtime calls are able to locate a pointer to
`rust_task` in TLS, and of course that pointer must be set up and
managed correctly. It's not something you can do manually.
Is it possible to initialize a pthread like rust_pthread_init() so that
later on it has a task context?
We are slowly working on 'freestanding Rust', which will let you run
Rust code without the runtime. This is particularly needed at the
moment to port the Rust runtime to Rust. The first steps are in this
pull request:
https://github.com/mozilla/rust/pull/4619
After that pull request you can make foreign calls and use the
exchange heap outside of task context, but if you do call a function
that requires the runtime the process will abort.
Very interesting...
I think any memory allocation will require the runtime, no? Though, it's
easy to call malloc() for libc...
Thanks for your answers,
Michael
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev