mozilla/rust — pull request #2261 by olsonjeffery (2012-04-20 18:19:44+00:00)
Title: std::uv::global_loop and std::timer

At a high-level, this pull request contains:

* fleshing out the `std::uv::hl` module (provides stuff needed to run a "high-level", rust-managed loop with hooks for user-supplied logic/environment
* adding the `std::uv::global_loop` module, which provides a single, process-wide event loop leverage the types and functions built out in `std::uv::hl`
* adding the `std::timer` module, which uses `std::uv::global_loop` to provide a high-level interface to the libuv `uv_timer_*` API.

### std::uv::hl

A set of functions and types to interact safely with a libuv, primarily from rust code. The test and functions in the test module provide a pretty good overview of how to do work it (including the bare minimum to roll-your-own `high_level_loop`)

### std::uv::global_loop

As mentioned above, this is the process-wide event loop that (presumably) stdlib developers can use to expose high-level functionality to libuv.

There are two, full implementations that are present at this time:

* The `get_single_task_gl` function (exported but hidden in docs) provides a libuv loop that consumes a single task/single-threaded-scheduler. This was my original implementation and, while (ostensibly) lighter-weighter in message/task traffic, it most likely has some race conditions.
* The `get_monitor_task_gl` function (also exported but hidden in docs) gives access to a libuv loop that's implemented over two tasks (each running in their own single-threaded scheduler). This is the implementation that is wired up to `uv::global_loop::get()`, which is the only fn from this module that appears in the docs

I want to work on the single-task global loop a bit more and, eventually, set up some profiling between the two versions (to test under load). But for now we're shipping what (should) be a race-free version.

NOTE: Do _not_ use both of the above functions within the scope of a single-process lifetime (always use one or the other, but not both). Using just `std::uv::global_loop::get` should (obviously) be your first choice 99.999999% of the time.

If we're satisfied, relatively speaking, with the perf of the `get_monitor_task_gl()` impl I'll just drop the `get_single_task_loop` impl.

### std::timer

Provides a few useful functions that use libuv's timer API. Currently only three exported functions:

* `timer::delayed_send` - send a msg after the provided timeout
* `timer::sleep` - block the task this is called in for the specified time period
* `timer::recv_timeout` - block on recv for up to the specified timeout. If a msg is recv'd on the provided port before the timeout expires, we return `some(T)`, other if the timeout period passes without a msg on the provided channel, we return `none`.

Interestingly, it seems like `timer::delayed_send` is the primitive upon which you can build everything else in the module, AFAIK.

I also wanted to add a more tradition timer interface that just takes a cb and calls it repeatedly in a new task until it returns false, but didn't get around to it.

The implementation, here, is the first non-infrastructure/proof-of-concept use of `std::uv::*`. hooray!

### still unresolved

* The management of libuv structs' lifetime, stored on the rust stack, is ackward and does not map cleanly to the C/C++ memery (of course!). So I've been trying to work on abstractions to provide some sort of safety net around this. We also have to integrate the `high_level_loop`'s internal reference counting scheme (that sits atop the libuv refcount scheme). I think I have something in mind (a resource, stored in a shared box, that will tie the struct value to a given task.. we some automated setup/teardown), but it's not quite ironed out yet (and places its own burden on the user). For now, we have the `uv::hl::ref`, `uv::hl::unref` and `uv::hl::unref_and_close` fns. Any code that uses libuv structs, on the stack should make to:
* `unref` and `uv_close` before the task containing the struct exits. This is not always straightforward and requires some thoughtful synchronization via ports/msgs that I'm still working. The tests in `hl`, `global_loop` and the impl in `std::timer` should different examples/approaches for doing this safely. It's an ongoing thing.
* Should this move into `core`? Probably want to let it prove itself, first.

### up next

* Iron-out remaining rough edges in the API and implementation
* work on getting profiling set up for the loop in general, as well as specific parts of the API.
* The low-level plumbing is in place for IPv4 TCP/IP, so I'll move towards flushing that out in a high-level API build around port/chan data passing (this work is blocked, on 32bit linux at least, pending #2064)
* Work towards high-level HTTP client and server scenarios leveraging a high-level TCP/IP API.
* Bind more of the libuv API's surface, in general, to rust (in `std::uv::ll`) and write tests
* More tests, better docs